51

ご存知のように、数字は数字で書くことも、名前で呼ぶこともできます。123を123に変換する例はたくさんありますが、逆に変換する方法の良い例は見つかりませんでした。

注意点のいくつか:

  1. 枢機卿/名目または序数:「1つ」および「最初」
  2. 一般的なスペルミス:「40」/「40」
  3. 数百/千:2100->「二十百」そして「二千百」
  4. 区切り文字:「112​​5」だけでなく、「1125」または「1125」など
  5. 口語表現:「30代」
  6. 分数:「3分の1」、「5分の2」
  7. 一般名:「ダース」、「ハーフ」

そして、おそらくまだリストされていない可能性のあるより多くの警告があります。アルゴリズムが非常に堅牢である必要があり、スペルミスさえ理解している必要があるとします。

これらすべての書き方を学ぶために、どの分野/論文/研究/アルゴリズムを読む必要がありますか?情報はどこにありますか?

PS:私の最後のパーサーは、実際には英語、ロシア語、ヘブライ語の3つの異なる言語を理解する必要があります。そして、おそらく後の段階で、より多くの言語が追加されるでしょう。ヘブライ語には男性/女性の番号もあります。たとえば、「1人の男性」と「1人の女性」には異なる「1つ」があります—「ehad」と「ahat」。ロシア語にも独自の複雑さがいくつかあります。

グーグルはこれで素晴らしい仕事をしています。例えば:

http://www.google.com/search?q=two+thousand+and+one+hundred+plus+five+dozen+and+four+fifths+in+decimal

(逆も可能ですhttp://www.google.com/search?q=999999999999+in+english

4

12 に答える 12

46

私は PEG パーサーをいじって、英語、スペイン語、および少なくともドイツ人。

たとえば、英語を扱う場合、単語を値に明確な方法でマッピングする辞書が必要です。

"one" -> 1, "two" -> 2, ... "twenty" -> 20,
"dozen" -> 12, "score" -> 20, ...
"hundred" -> 100, "thousand" -> 1000, "million" -> 1000000

...など

アルゴリズムは次のとおりです。

total = 0
prior = null
for each word w
    v <- value(w) or next if no value defined
    prior <- case
        when prior is null:       v
        when prior > v:     prior+v
        else                prior*v
        else
    if w in {thousand,million,billion,trillion...}
        total <- total + prior
        prior <- null
total = total + prior unless prior is null

たとえば、これは次のように進行します。

total    prior      v     unconsumed string
    0      _              four score and seven 
                    4     score and seven 
    0      4              
                   20     and seven 
    0     80      
                    _     seven 
    0     80      
                    7 
    0     87      
   87

total    prior      v     unconsumed string
    0        _            two million four hundred twelve thousand eight hundred seven
                    2     million four hundred twelve thousand eight hundred seven
    0        2
                  1000000 four hundred twelve thousand eight hundred seven
2000000      _
                    4     hundred twelve thousand eight hundred seven
2000000      4
                    100   twelve thousand eight hundred seven
2000000    400
                    12    thousand eight hundred seven
2000000    412
                    1000  eight hundred seven
2000000  412000
                    1000  eight hundred seven
2412000     _
                      8   hundred seven
2412000     8
                     100  seven
2412000   800
                     7
2412000   807
2412807

等々。私はそれが完璧だと言っているわけではありません。


編集時に特定のリストに対処する:

  1. cardinal/nominal or ordinal: "one" and "first" --辞書に入れるだけ
  2. 英語/イギリス: "fourty"/"forty" --同上
  3. 数百/数千: 2100 -> 「2100」および「2000 100」 --そのまま動作
  4. 区切り記号: 「1152」だけでなく、「1152」や「1152」などもあります。「次の単語」を、定義された単語に一致する最長の接頭辞、または次の単語までに定義するだけです。最初に、何もしない場合は非単語
  5. 口語表現: "thirty-something" --動作します
  6. フラグメント: 「3 分の 1」、「5 分の 2」 --ええと、まだ...
  7. 一般的な名前: 「ダース」、「半分」 --動作します。「半ダース」のようなこともできます

数字の 6 は、私がすぐに答えを出せない唯一の数字です。これは、序数と分数の間のあいまいさが (少なくとも英語では)、最後にコーヒーを 1 杯飲んだのが時間も前だったという事実が原因です。

于 2009-03-17T05:28:46.030 に答える
12

これは簡単な問題ではなく、それを行うライブラリがないことを私は知っています。いつか落ち着いて、このようなことを書こうとするかもしれません。ただし、Prolog、Java、または Haskell のいずれかで行います。私が見る限り、いくつかの問題があります。

  • トークン化: 数字は 1152 と書かれることもありますが、私は 1152 や 1152 などを見たことがあります。実際にどのフォームが使用されているかを調査する必要があります。これは、ヘブライ語にとって特にトリッキーかもしれません。
  • スペルミス: それほど難しいことではありません。言葉の量は限られていますが、少しのレーベンシュタイン距離魔法でうまくいくはずです。
  • すでに述べたように、代替形式が存在します。これには、序数/基数だけでなく、40/40 および...
  • ... 一般的な名前または一般的に使用されるフレーズと NE (名前付きエンティティ)。三十年戦争から 30 人、第二次世界大戦から 2 人、どちらを引き抜きたいですか?
  • ローマ数字も?
  • 「30 代」や「3 ユーロと榴散弾」などの口語表現。

これに興味があるなら、今週末試してみよう。私の考えは、おそらく UIMA を使用してそれをトークン化し、さらにトークン化/曖昧さを取り除き、最終的に翻訳することです。他にも問題があるかもしれません。もっと面白いことを思いつくことができるか見てみましょう。

申し訳ありませんが、これはまだ本当の答えではなく、質問の延長に過ぎません。何か見つけたり書いたりしたらお知らせします。

ところで、数字のセマンティクスに興味がある方のために、フリーデリケ モルトマンの興味深い論文を見つけました。数字の論理解釈に関するいくつかの問題が論じられています。

于 2008-09-18T00:26:19.363 に答える
11

少し前に書いたコードがあります: text2num。これは、序数を処理しないことを除いて、必要なことの一部を行います。私は実際にこのコードを何にも使用していないので、ほとんどテストされていません!

于 2008-09-16T07:52:42.817 に答える
7

Python pattern-enライブラリを使用します。

>>> from pattern.en import number
>>> number('two thousand fifty and a half') => 2050.5
于 2011-08-03T11:39:30.363 に答える
5

ヨーロッパとアメリカではカウントが異なることに注意してください。

欧州規格:

One Thousand
One Million
One Thousand Millions (British also use Milliard)
One Billion
One Thousand Billions
One Trillion
One Thousand Trillions

ここにそれに関する小さなリファレンスがあります。


違いを確認する簡単な方法は次のとおりです。

(American counting Trillion) == (European counting Billion)
于 2009-03-17T14:06:00.437 に答える
4

序数は、言語の他の数字と意味のある方法で結合できないため、適用されません (... 少なくとも英語では)

例: 100 と 1 秒、11 秒など...

ただし、「and」という言葉には別の英語/アメリカの警告があります。

すなわち

100 と 1 (英語) 100 1 (アメリカ)

また、英語で 1 を意味する 'a' の使用

千 = 千

...ちなみに、Google の電卓はこれを驚くほどうまく処理します。

光速の13万倍

さらには...

二千百プラスダース

…えっ!? スコアとローマ数字のダース

于 2009-03-14T00:56:06.637 に答える
3

これがClojureの非常に堅牢なソリューションです。

AFAIKは、独自の実装アプローチです。

;----------------------------------------------------------------------
; numbers.clj
; written by: Mike Mattie codermattie@gmail.com
;----------------------------------------------------------------------
(ns operator.numbers
  (:use compojure.core)

  (:require
    [clojure.string     :as string] ))

(def number-word-table {
  "zero"          0
  "one"           1
  "two"           2
  "three"         3
  "four"          4
  "five"          5
  "six"           6
  "seven"         7
  "eight"         8
  "nine"          9
  "ten"           10
  "eleven"        11
  "twelve"        12
  "thirteen"      13
  "fourteen"      14
  "fifteen"       15
  "sixteen"       16
  "seventeen"     17
  "eighteen"      18
  "nineteen"      19
  "twenty"        20
  "thirty"        30
  "fourty"        40
  "fifty"         50
  "sixty"         60
  "seventy"       70
  "eighty"        80
  "ninety"        90
})

(def multiplier-word-table {
  "hundred"       100
  "thousand"      1000
})

(defn sum-words-to-number [ words ]
  (apply + (map (fn [ word ] (number-word-table word)) words)) )

; are you down with the sickness ?
(defn words-to-number [ words ]
  (let
    [ n           (count words)

      multipliers (filter (fn [x] (not (false? x))) (map-indexed
                                                      (fn [ i word ]
                                                        (if (contains? multiplier-word-table word)
                                                          (vector i (multiplier-word-table word))
                                                          false))
                                                      words) )

      x           (ref 0) ]

    (loop [ indices (reverse (conj (reverse multipliers) (vector n 1)))
            left    0
            combine + ]
      (let
        [ right (first indices) ]

        (dosync (alter x combine (* (if (> (- (first right) left) 0)
                                      (sum-words-to-number (subvec words left (first right)))
                                      1)
                                    (second right)) ))

        (when (> (count (rest indices)) 0)
          (recur (rest indices) (inc (first right))
            (if (= (inc (first right)) (first (second indices)))
              *
              +))) ) )
    @x ))

下記は用例です

(operator.numbers/words-to-number ["six" "thousand" "five" "hundred" "twenty" "two"])
(operator.numbers/words-to-number ["fifty" "seven" "hundred"])
(operator.numbers/words-to-number ["hundred"])
于 2011-11-13T14:16:12.323 に答える
2

さて、私はこの質問の答えに遅すぎましたが、私は私にとって非常にうまくいったように見える小さなテストシナリオを働いていました。(単純ですが、醜く、大きい)正規表現を使用して、すべての単語を検索しました。式は次のとおりです。

(?<Value>(?:zero)|(?:one|first)|(?:two|second)|(?:three|third)|(?:four|fourth)|
(?:five|fifth)|(?:six|sixth)|(?:seven|seventh)|(?:eight|eighth)|(?:nine|ninth)|
(?:ten|tenth)|(?:eleven|eleventh)|(?:twelve|twelfth)|(?:thirteen|thirteenth)|
(?:fourteen|fourteenth)|(?:fifteen|fifteenth)|(?:sixteen|sixteenth)|
(?:seventeen|seventeenth)|(?:eighteen|eighteenth)|(?:nineteen|nineteenth)|
(?:twenty|twentieth)|(?:thirty|thirtieth)|(?:forty|fortieth)|(?:fifty|fiftieth)|
(?:sixty|sixtieth)|(?:seventy|seventieth)|(?:eighty|eightieth)|(?:ninety|ninetieth)|
(?<Magnitude>(?:hundred|hundredth)|(?:thousand|thousandth)|(?:million|millionth)|
(?:billion|billionth)))

ここでは、フォーマットのために改行を付けて示しています。

とにかく、私の方法は、PCREのようなライブラリでこの正規表現を実行してから、名前付きの一致を読み戻すことでした。そして、私が追加しなかったので、「半分」のタイプを除いて、この質問にリストされたさまざまな例のすべてで機能しましたが、ご覧のとおり、そうするのは難しいことではありません。これは多くの問題に対処します。たとえば、元の質問とその他の回答の次の項目に対応しています。

  1. 枢機卿/名目または序数:「1つ」および「最初」
  2. よくあるスペルミス:「40」/「40」(これは明示的に対処されていないことに注意してください。これは、文字列をこのパーサーに渡す前に実行したいことです。このパーサーは、この例を「FOUR」と見なします。 ..)
  3. 数百/千:2100->「二十百」そして「二千百」
  4. 区切り文字:「112​​5」だけでなく、「1125」または「1125」など
  5. コロキアリズム:「30-何か」(これも「何か」とは何かとして、完全には扱われていませんか?まあ、このコードはこの数字を単に「30」として見つけます)。**

さて、この正規表現のモンスターをソースに保存するのではなく、次のようなものを使用して、実行時にこのRegExを構築することを検討しました。

char *ones[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve",
  "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
char *tens[] = {"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
char *ordinalones[] = { "", "first", "second", "third", "fourth", "fifth", "", "", "", "", "", "", "twelfth" };
char *ordinaltens[] = { "", "", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth" };
and so on...

ここでの簡単な部分は、重要な単語のみを保存していることです。SIXTHの場合、THが付けられた通常の番号であるため、エントリがないことに気付くでしょう...しかし、TWELVEのようなものは別の注意が必要です。

これで、(醜い)正規表現を作成するためのコードができました。これで、数値文字列に対して実行するだけです。

私がお勧めすることの1つは、「AND」という単語をフィルタリングまたは食べることです。それは必要ではなく、他の問題につながるだけです。

したがって、実行したいのは、「マグニチュード」の名前付き一致をすべての可能なマグニチュード値を調べる関数に渡し、現在の結果にそのマグニチュードの値を乗算する関数を設定することです。次に、matchesという名前の「Value」を調べ、そこで検出された値に基づいてint(または使用しているもの)を返す関数を作成します。

すべてのVALUEの一致が結果に追加され、magnitutdeの一致は結果にmag値を掛けます。つまり、25万は「2」、「2 * 100」、「200 + 50」、「250 * 1000」の順になり、250000になります。

楽しみのために、私はこれのvbScriptバージョンを作成しましたが、提供されているすべての例でうまく機能しました。現在、名前付き一致はサポートされていないため、正しい結果を得るにはもう少し努力する必要がありましたが、うまくいきました。結論として、「VALUE」が一致する場合は、アキュムレータを追加します。マグニチュードが一致する場合は、アキュムレータに100、1000、1000000、1000000000などを掛けます。これにより、かなり驚くべき結果が得られます。「半分」などを調整するために必要なのは、それらを追加することだけです。あなたの正規表現に、それらのコードマーカーを入れて、それらを処理します。

さて、この投稿が誰かに役立つことを願っています。誰かが望むなら、私がこれをテストするために使用したvbScript擬似コードで投稿することができますが、それはきれいなコードではなく、本番コードでもありません。

可能であれば..これが書かれる最終的な言語は何ですか?C ++、またはスクリプト言語のようなもの?Greg Hewgillの情報源は、これらすべてがどのように組み合わされるかを理解するのに大いに役立ちます。

他にご不明な点がありましたらお知らせください。申し訳ありませんが、私は英語/アメリカ人しか知らないので、他の言語についてはお手伝いできません。

于 2009-03-20T23:33:46.087 に答える
2

あなたの要件のいくつかの私の LPC 実装 (アメリカ英語のみ):

internal mapping inordinal = ([]);
internal mapping number = ([]);

#define Numbers ([\
    "zero"        : 0, \
    "one"         : 1, \
    "two"         : 2, \
    "three"       : 3, \
    "four"        : 4, \
    "five"        : 5, \
    "six"         : 6, \
    "seven"       : 7, \
    "eight"       : 8, \
    "nine"        : 9, \
    "ten"         : 10, \
    "eleven"      : 11, \
    "twelve"      : 12, \
    "thirteen"    : 13, \
    "fourteen"    : 14, \
    "fifteen"     : 15, \
    "sixteen"     : 16, \
    "seventeen"   : 17, \
    "eighteen"    : 18, \
    "nineteen"    : 19, \
    "twenty"      : 20, \
    "thirty"      : 30, \
    "forty"       : 40, \
    "fifty"       : 50, \
    "sixty"       : 60, \
    "seventy"     : 70, \
    "eighty"      : 80, \
    "ninety"      : 90, \
    "hundred"     : 100, \
    "thousand"    : 1000, \
    "million"     : 1000000, \
    "billion"     : 1000000000, \
])

#define Ordinals ([\
    "zeroth"        : 0, \
    "first"         : 1, \
    "second"        : 2, \
    "third"         : 3, \
    "fourth"        : 4, \
    "fifth"         : 5, \
    "sixth"         : 6, \
    "seventh"       : 7, \
    "eighth"        : 8, \
    "ninth"         : 9, \
    "tenth"         : 10, \
    "eleventh"      : 11, \
    "twelfth"       : 12, \
    "thirteenth"    : 13, \
    "fourteenth"    : 14, \
    "fifteenth"     : 15, \
    "sixteenth"     : 16, \
    "seventeenth"   : 17, \
    "eighteenth"    : 18, \
    "nineteenth"    : 19, \
    "twentieth"     : 20, \
    "thirtieth"     : 30, \
    "fortieth"      : 40, \
    "fiftieth"      : 50, \
    "sixtieth"      : 60, \
    "seventieth"    : 70, \
    "eightieth"     : 80, \
    "ninetieth"     : 90, \
    "hundredth"     : 100, \
    "thousandth"    : 1000, \
    "millionth"     : 1000000, \
    "billionth"     : 1000000000, \
])

varargs int denumerical(string num, status ordinal) {
    if(ordinal) {
        if(member(inordinal, num))
            return inordinal[num];
    } else {
        if(member(number, num))
            return number[num];
    }
    int sign = 1;
    int total = 0;
    int sub = 0;
    int value;
    string array parts = regexplode(num, " |-");
    if(sizeof(parts) >= 2 && parts[0] == "" && parts[1] == "-")
        sign = -1;
    for(int ix = 0, int iix = sizeof(parts); ix < iix; ix++) {
        string part = parts[ix];
        switch(part) {
        case "negative" :
        case "minus"    :
            sign = -1;
            continue;
        case ""         :
            continue;
        }
        if(ordinal && ix == iix - 1) {
            if(part[0] >= '0' && part[0] <= '9' && ends_with(part, "th"))
                value = to_int(part[..<3]);
            else if(member(Ordinals, part))
                value = Ordinals[part];
            else
                continue;
        } else {
            if(part[0] >= '0' && part[0] <= '9')
                value = to_int(part);
            else if(member(Numbers, part))
                value = Numbers[part];
            else
                continue;
        }
        if(value < 0) {
            sign = -1;
            value = - value;
        }
        if(value < 10) {
            if(sub >= 1000) {
                total += sub;
                sub = value;
            } else {
                sub += value;
            }
        } else if(value < 100) {
            if(sub < 10) {
                sub = 100 * sub + value;
            } else if(sub >= 1000) {
                total += sub;
                sub = value;
            } else {
                sub *= value;
            }
        } else if(value < sub) {
            total += sub;
            sub = value;
        } else if(sub == 0) {
            sub = value;
        } else {
            sub *= value;
        }
    }
    total += sub;
    return sign * total;
}
于 2009-03-17T05:43:39.130 に答える
0

私は初期の現代の書籍 (「第 2 版」、「Editio quarta」など) の序数版ステートメントを整数に変換していましたが、英語では序数 1 から 100 をサポートし、いくつかのロマンス言語では序数 1 から 10 をサポートする必要がありました。これが私がPythonで思いついたものです:

def get_data_mapping():
  data_mapping = {
    "1st": 1,
    "2nd": 2,
    "3rd": 3,

    "tenth": 10,
    "eleventh": 11,
    "twelfth": 12,
    "thirteenth": 13,
    "fourteenth": 14,
    "fifteenth": 15,
    "sixteenth": 16,
    "seventeenth": 17,
    "eighteenth": 18,
    "nineteenth": 19,
    "twentieth": 20,

    "new": 2,
    "newly": 2,
    "nova": 2,
    "nouvelle": 2,
    "altera": 2,
    "andere": 2,

    # latin
    "primus": 1,
    "secunda": 2,
    "tertia": 3,
    "quarta": 4,
    "quinta": 5,
    "sexta": 6,
    "septima": 7,
    "octava": 8,
    "nona": 9,
    "decima": 10,

    # italian
    "primo": 1,
    "secondo": 2,
    "terzo": 3,
    "quarto": 4,
    "quinto": 5,
    "sesto": 6,
    "settimo": 7,
    "ottavo": 8,
    "nono": 9,
    "decimo": 10,

    # french
    "premier": 1,
    "deuxième": 2,
    "troisième": 3,
    "quatrième": 4,
    "cinquième": 5,
    "sixième": 6,
    "septième": 7,
    "huitième": 8,
    "neuvième": 9,
    "dixième": 10,

    # spanish
    "primero": 1,
    "segundo": 2,
    "tercero": 3,
    "cuarto": 4,
    "quinto": 5,
    "sexto": 6,
    "septimo": 7,
    "octavo": 8,
    "noveno": 9,
    "decimo": 10
  }

  # create 4th, 5th, ... 20th
  for i in xrange(16):
    data_mapping[str(4+i) + "th"] = 4+i

  # create 21st, 22nd, ... 99th
  for i in xrange(79):
    last_char = str(i)[-1]

    if last_char == "0":
      data_mapping[str(20+i) + "th"] = 20+i

    elif last_char == "1":
      data_mapping[str(20+i) + "st"] = 20+i

    elif last_char == "2":
      data_mapping[str(20+i) + "nd"] = 20+i

    elif last_char == "3":
      data_mapping[str(20+i) + "rd"] = 20+i

    else:
      data_mapping[str(20+i) + "th"] = 20+i

  ordinals = [
    "first", "second", "third", 
    "fourth", "fifth", "sixth", 
    "seventh", "eighth", "ninth"
  ]

  # create first, second ... ninth
  for c, i in enumerate(ordinals):
    data_mapping[i] = c+1

  # create twenty-first, twenty-second ... ninty-ninth
  for ci, i in enumerate([
    "twenty", "thirty", "forty", 
    "fifty", "sixty", "seventy", 
    "eighty", "ninety"
  ]):
    for cj, j in enumerate(ordinals):
      data_mapping[i + "-" + j] = 20 + (ci*10) + (cj+1)
    data_mapping[i.replace("y", "ieth")] = 20 + (ci*10)

  return data_mapping
于 2016-12-29T21:29:39.090 に答える
-2

探し始める場所の1つは、gnu get_date libです。これは、ほぼすべての英語のテキスト日付をタイムスタンプに解析できます。正確にはあなたが探しているものではありませんが、同様の問題に対する彼らの解決策は多くの有用な手がかりを提供する可能性があります。

于 2009-03-17T14:00:02.540 に答える