Domanda

Come tutti sappiamo i numeri possono essere scritti sia in valori numerici, o chiamati con il loro nome.Mentre ci sono un sacco di esempi, che la conversione 123 in centoventi tre, io non riuscivo a trovare buoni esempi di come convertirlo in un altro modo.

Alcune avvertenze:

  1. il cardinale/valore nominale o ordinale:"uno" e "prima"
  2. errori di ortografia comuni:"quaranta"/"quaranta"
  3. centinaia/migliaia:2100 -> "venti cento" e anche "due mila e cento"
  4. separatori:"mille e cento cinquanta due", ma anche "elevenhundred cinquantadue" o "undici-cento cinquanta-due" e quant'altro
  5. colloquiali:"trenta-qualcosa"
  6. frazioni:'un terzo', 'due quinti'
  7. nomi comuni:'dozzina', 'mezzo'

E probabilmente ci sono più le avvertenze possibile che non sono ancora iscritti.Supponiamo che l'algoritmo deve essere molto robusta, e nemmeno a capire gli errori di ortografia.

Quali campi/documenti/studi/algoritmi devo leggere per imparare a scrivere tutto questo?Dov'è l'informazione?

PS:Il mio finale parser dovrebbe comprendere effettivamente le 3 lingue diverse, inglese, russo ed ebraico.E, forse, in una fase successiva più lingue saranno aggiunte.L'ebraico ha anche maschio/femmina numeri, come "un uomo" e "una donna" un diverso "si" — "ehad" e "ahat".Russo ha anche un po ' della loro complessità.

Google fa un grande lavoro a questo.Per esempio:

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

(il contrario è anche possibile http://www.google.com/search?q=999999999999+in+english)

È stato utile?

Soluzione

Sono stato a giocare con un PEG di parser per fare quello che si voleva (e possono inviare il che come una risposta separato dopo), quando ho notato che c'è un algoritmo molto semplice che fa un ottimo lavoro con le forme comuni di numeri in inglese, spagnolo e tedesco, almeno.

La collaborazione con l'inglese, per esempio, avete bisogno di un dizionario che mappa le parole di valori in modo evidente:

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

...e così via

L'algoritmo:

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

Per esempio, questo si svolge, come segue:

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

E così via.Non sto dicendo che è perfetto, ma per un rapido e sporco lo fa abbastanza bene.


Affronta il tuo elenco specifico su modifica:

  1. il cardinale/valore nominale o ordinale:"uno" e "prima" -- basta metterli nel dizionario
  2. inglese/inglese:"quaranta"/"quaranta" -- idem
  3. centinaia/migliaia:2100 -> "venti cento" e anche "due mila e cento" -- funziona così com'è
  4. separatori:"mille e cento cinquanta due", ma anche "elevenhundred cinquantadue" o "undici-cento cinquanta-due" e quant'altro - basta definire "parola" per essere il più lungo prefisso che corrisponde a un definito parola, o fino al prossimo non-parola, se nessuno fare, per cominciare
  5. colloqialisms:"trenta-qualcosa" -- funziona
  6. frammenti:'un terzo', 'due quinti' -- uh, non ancora...
  7. nomi comuni:'dozzina', 'mezzo' -- opere;si può anche fare cose come "una mezza dozzina"

Il numero 6 è l'unico a non avere una risposta pronta per, e che a causa dell'ambiguità tra ordinali e frazioni (in inglese almeno) aggiunto al fatto che la mia ultima tazza di caffè è stato molti ore fa.

Altri suggerimenti

Non è un argomento facile, e so di nessuna libreria di farlo.Mi potrebbe sedersi e provare a scrivere qualcosa di simile a questo momento.Mi piacerebbe farlo in Prolog, Java o Haskell, però.Per quanto posso vedere, ci sono diversi problemi:

  • Suddivisione:a volte, i numeri sono scritti mille e cento cinquanta due, ma ho visto elevenhundred cinquantadue o undici-cento-cinquanta-due e quant'altro.Bisognerebbe fare un sondaggio su quali sono le forme sono effettivamente in uso.Questo potrebbe essere particolarmente difficile per l'ebraico.
  • Gli errori di ortografia:non è così difficile.Hai un numero limitato di parole, e un po ' di Levenshtein distanza magia dovrebbe fare il trucco.
  • Forme alternative, come si è già accennato, esiste.Questo include ordinale o cardinale numeri, così come quaranta/quaranta e...
  • ...nomi comuni o frasi di uso comune e NEs (entità con nome).Vuoi estrarre 30 dalla Guerra dei Trent'Anni o 2 dalla seconda Guerra Mondiale?
  • Numeri romani, troppo?
  • Colloqialisms, come "trenta-qualcosa" e "tre Euro e schegge", che non saprei come trattare.

Se siete interessati a questo, ho potuto dare un colpo di questo fine settimana.La mia idea è probabilmente utilizzando UIMA e la creazione di token con esso, per poi ulteriormente simboleggiare/ambiguità e, infine, tradurre.Ci potrebbero essere ulteriori problemi, vediamo se riesco a venire con alcune cose più interessanti.

Mi dispiace, questa non è una vera risposta, solo un'estensione per la tua domanda.Ti farò sapere se trovo/scrivere qualcosa.

A proposito, se siete interessati alla semantica dei numeri, ho appena trovato un carta interessante da Friederike Moltmann, discutere di alcuni problemi per quanto riguarda la logica di interpretazione dei numeri.

Ho un po 'di codice che ho scritto un po' di tempo fa: text2num.Questo fa parte di ciò che si desidera, a meno che non gestire i numeri ordinali.Io in realtà non ho usato questo codice per niente, quindi è in gran parte inedite!

Uso il Python modello-it libreria:

>>> from pattern.en import number
>>> number('two thousand fifty and a half') => 2050.5

Si dovrebbe tenere a mente che l'Europa e l'America contano in modo diverso.

Standard europeo:

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

Qui è un piccolo riferimento su di esso.


Un modo semplice per vedere la differenza è la seguente:

(American counting Trillion) == (European counting Billion)

I numeri ordinali sono non applicabile in quanto non possono essere uniti in modo significativo con altri numeri nella lingua (...almeno in inglese)

ad es.cento e primo, undici seconda, ecc...

Tuttavia, c'è un altro inglese/Americano avvertenza con la parola 'e'

cioè

cento e uno (in inglese) centouno (Americano)

Inoltre, l'uso di " a " significa in inglese

mille = mille

...Su una nota di parte di Google calcolatrice fa un lavoro incredibile di questo.

cento e tre migliaia di volte la velocità della luce

E anche...

due mila e cento e più di una dozzina di

...wtf?!? un punteggio più una decina in numeri romani

Qui è estremamente robusta soluzione in Clojure.

Per quanto ne so è un unico approccio di implementazione.

;----------------------------------------------------------------------
; 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 ))

Ecco alcuni esempi

(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"])

Il mio LPC attuazione di alcune delle vostre esigenze Americana (solo in inglese):

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;
}

Beh, era troppo tardi per la risposta per questa domanda, ma ho lavorato un po ' di test scenario che sembra aver funzionato molto bene per me.Ho usato un (semplice, ma di brutto, e grandi) espressione regolare per individuare tutte le parole per me.L'espressione è la seguente:

(?<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)))

Indicato qui con interruzioni di riga per motivi di formattazione..

Comunque, il mio metodo era quello di eseguire questa RegEx con una libreria PCRE, e quindi leggere il nome corrisponde.E ha funzionato su tutti i diversi esempi elencati in questa domanda, meno la "Metà", tipi, come non mi aggiungerli, ma come si può vedere, non sarebbe difficile farlo.Questo risolve un sacco di problemi.Per esempio, affronta i seguenti elementi per la domanda originale e altre risposte:

  1. il cardinale/valore nominale o ordinale:"uno" e "prima"
  2. errori di ortografia comuni:"quaranta"/"quaranta" (Nota che non affronta ESPLICITAMENTE questo, che sarebbe qualcosa che si vuole fare prima è passata la stringa di questo parser.Questo parser vede in questo esempio, come "QUATTRO"...)
  3. centinaia/migliaia:2100 -> "venti cento" e anche "due mila e cento"
  4. separatori:"mille e cento cinquanta due", ma anche "elevenhundred cinquantadue" o "undici-cento cinquanta-due" e quant'altro
  5. colloqialisms:"trenta-qualcosa" (anche Questo non è assolutamente indirizzato, come ciò che È "qualcosa"?Bene, questo codice si trova questo numero, semplicemente come "30").**

Ora, piuttosto che memorizzare questo mostro di una espressione regolare in origine, stavo pensando di costruire questa RegEx in fase di esecuzione, utilizzando qualcosa di simile al seguente:

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...

La parte facile è che sono solo memorizzare le parole che contano.Nel caso di SESTO, si noterà che non c'è una voce per esso, perché è solo un normale numero di TH appiccicato...Ma quelli, come DODICI bisogno di diversi attenzione.

Ok, così ora abbiamo il codice per costruire il nostro (brutto) RegEx, ora dobbiamo solo eseguire sul nostro numero di stringhe.

Una cosa mi sento di raccomandare, è quello di filtro, o mangiare la parola "E".Non è necessario, e solo porta ad altri problemi.

Così, che cosa si sta andando a voler fare è impostare una funzione che passa il nome corrispondenze per "Grandezza" in una funzione che cerca a tutti i possibili valori di magnitudo, e si moltiplica il risultato corrente da che valore di grandezza.Quindi, è possibile creare una funzione che guarda il "Valore" di nome corrisponde, e restituisce un int (o qualunque cosa si sta utilizzando), in base al valore scoperti lì.

Tutti VALORE corrisponde vengono AGGIUNTI al risultato, mentre magnitutde corrisponde a moltiplicare il risultato per il mag valore.Così, duecento Cinquanta Mila diventa "2", poi "2 * 100", quindi "200 + 50", quindi "250 * 1000", per finire con 250000...

Solo per divertimento, ho scritto un vbScript versione di questo e ha funzionato alla grande con tutti gli esempi forniti.Ora, non supporta denominato partite, quindi ho dovuto lavorare un po ' più difficile ottenere il risultato corretto, ma ho ottenuto.Linea di fondo è, se si tratta di un "VALORE" match, aggiungerlo al tuo accumulatore.Se si tratta di un ordine di grandezza partita, moltiplicare il vostro accumulatore 100, 1000, 1000000, 1000000000, ecc...Questo vi fornirà alcuni risultati piuttosto sorprendenti, e tutto quello che dovete fare per regolare le cose come "metà" è aggiungerli alla tua RegEx, mettere in un indicatore di codice per loro, e la loro gestione.

Bene, spero che questo post aiuta QUALCUNO là fuori.Se qualcuno vuole, posso inviare da vbScript pseudo codice che ho usato per prova di questo, tuttavia, non è piacevole codice, e NON il codice di produzione.

Se posso..Qual è il finale di questa lingua sarà scritto?C++, o qualcosa di simile a uno script di lingua?Greg Hewgill sorgente andrà un lungo cammino per aiutare a capire come tutto questo viene insieme.

Fatemi sapere se posso essere di nessun aiuto.Mi dispiace, so solo l'inglese/Americano, quindi non ti posso aiutare con le altre lingue.

Ero la conversione ordinale edizione dichiarazioni dei primi libri moderni (ad es."2 ° edizione", "Editio quarta") di numeri interi e il supporto necessario per i numeri ordinali da 1 a 100 in inglese e ordinali da 1 a 10 in un paio di lingue Romanze.Ecco cosa mi è venuta in 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

Provare

  1. Aprire una Richiesta HTTP "http://www.google.com/search?q="+ numero + "+in+decimale".

  2. Analizzare il risultato per il numero.

  3. Cache il numero di risultati / coppie a lezione di richieste di tempo.

Un posto per iniziare la ricerca è gnu get_date lib, che sia in grado di analizzare qualsiasi Inglese testuale data in timestamp.Mentre non è esattamente quello che stai cercando, la loro soluzione ad un problema simile potrebbe fornire un sacco di indizi utili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top