将英语转换为数字


27

对挑战的简短描述:

基于此站点上其他几个问题的想法,您面临的挑战是在任何程序中编写最具创意的代码,这些程序将以英文写成的数字输入并转换为整数形式。

真正干燥,漫长而彻底的规格:

  • 您的程序将在和之间以小写英语输入一个整数作为输入zeronine hundred ninety-nine thousand nine hundred ninety-nine含)的。
  • 它只能输出介于0和之间的数字的整数形式999999并且不能输出其他任何内容(没有空格)。
  • 输入将包含,and,如one thousand, two hundredfive hundred and thirty-two
  • 当十位数和1一位数均非零且十位数大于时,它们将由HYPHEN-MINUS字符-而不是空格分隔。同上一万个地方。例如,six hundred fifty-four thousand three hundred twenty-one
  • 该程序可能具有任何其他输入的未定义行为。

行为良好的程序的一些示例:

zero-> 0
fifteen-> 15
ninety-> 90
seven hundred four-> 704
sixty-nine thousand four hundred eleven-> 69411
five hundred twenty thousand two->520002


这不是特别有创意,也不完全符合这里的规范,但是它可能是一个有用的起点:github.com/ghewgill/text2num/blob/master/text2num.py
Greg Hewgill 2014年

我几乎可以发表我对这个问题的答案。
2014年

为什么要进行复杂的字符串解析?pastebin.com/WyXevnxb
blutorange

1
顺便说一句,我看到了一个IOCCC条目,它是该问题的答案。
小吃

2
像“四点二十”这样的事情呢?
蓬松的

Answers:


93

苹果脚本

一个愚蠢的,hacky的混搭可能会使Cupertino / Mountain View的某些人不高兴,但我认为这是一种创造性的愚蠢的,hacky的混搭。

set myNumber to text returned of (display dialog ¬
    "Enter number as text:" buttons {"Continue…"} ¬
    default answer "" default button 1)
tell application "Google Chrome"
    activate
    open location "https://www.google.com"
end tell
delay 5
say "ok google. " & myNumber
delay 2
tell application "System Events"
    tell application process "Google Chrome"
        set fullURL to value of text field 1 of toolbar 1 of window 1
    end tell
end tell
set AppleScript's text item delimiters to "="
display alert item 2 of text items of fullURL

使用OSX文本进行语音朗读以说出文本编号,然后使用Google音频搜索来侦听并将其转换为整数。

要求

  • OSX
  • 谷歌浏览器
  • 在您的Google帐户中启用了语音识别
  • 音量调到合理水平

延迟时间可能需要根据您的Chrome浏览器加载时间和Google查找时间进行调整。

输入示例:

在此处输入图片说明

输出示例:

在此处输入图片说明


13
认为这可能只是一点点创意...;)
亚伯拉罕

5
大声笑,这

2
也许太有创意了。
Cheezey 2014年

一个星期后,您的答案显然以74票的优势领先,所以我认为这代表您赢得胜利!顺便说一句,介意我是否使用此代码?对于我现在正在从事的许多现实世界项目而言,这将非常有用!;)
亚伯拉罕

3
@亚伯拉罕谢谢!您在开玩笑在生产代码中使用它,对吗?
Digital Trauma

34

Bash,93 64 55个字符*

bsd-games大多数Linux操作系统上可用的出色软件包中,有一个名为的小型命令行玩具number。它将数字转换为英文文本,也就是说,它与该问题完全相反。事实恰恰相反:问题中的所有规则都遵循number。巧合几乎太好了。

$ number 42
forty-two.

当然,number不回答这个问题。反过来,我们想要它。我考虑了一会儿,尝试了字符串解析等等,然后意识到我可以只调用number所有999.999的数字,看看是否有匹配的输入。如果是这样,则匹配的第一行的行号是我要查找的行号的两倍(number在每个数字后打印一行点)。就那么简单。因此,事不宜迟,这里是我输入的完整代码:

seq 0 999999|number -l|awk "/$1/{print (NR-1)/2;exit}"

它甚至会短路,因此转换“二”的速度非常快,通常在我的盒子上不到一秒钟的时间内解码出甚至更高的数字。这是一个示例运行:

wn@box /tmp> bash unnumber.sh "zero"
0
wn@box /tmp> bash unnumber.sh "fifteen"
15
wn@box /tmp> bash unnumber.sh "ninety" 
90
wn@box /tmp> bash unnumber.sh "seven hundred four"
704
wn@box /tmp> bash unnumber.sh "sixty-nine thousand four hundred eleven"
69411
wn@box /tmp> bash unnumber.sh "five hundred twenty thousand two"    
520002

当然,您需要 number安装此功能才能正常工作。


*:是的,我知道,这不是code-golf挑战,但是短缺是我参赛作品唯一可辨别的品质,所以... :)


8
+1。对我来说,number反向使用是此答案最具创意的事情。高尔夫也不错:)
Digital Trauma

1
这实际上很有创意!我喜欢!
sokie 2014年

13

Java脚本

(function parse(input) {
  var pat = "ze/on/tw/th.?r/fo/fi/ix/se/ei/ni/ten/ele".split("/");
  var num = "", last = 0, token = input.replace(/-/g, " ").split(" ");
  for(var i in token) {
    var t = token[i];
    for(var p in pat) if(t.match(RegExp(pat[p])) !== null) num += "+" + p;
    if(t.indexOf("een") >= 0) num += "+10";
    if(t.indexOf("lve") >= 0) num += "+10";
    if(t.indexOf("ty") >= 0) num += "*10";
    if(t.indexOf("dr") >= 0) { last = 100; num += "*100"; }
    if(t.indexOf("us") >= 0) {
      if(last < 1000) num = "(" + num + ")"; last = 0;
      num += "*1000";
    }
  }
  alert(eval(num));
})(prompt());

你喜欢一些吗 eval()吗?

在浏览器的控制台上运行此脚本。

编辑:感谢您的反馈。错误已修复(再次)。


非常好的代码^^
zsitro

2
当你输入的东西,如“116”,它会给126
scrblnrd3

该程序从twelve返回时开始的某些编号失败23
亚伯拉罕2014年

失败"twenty"
200_success 2014年

seven thousand three hundred thirty five给我10335
婴儿

7

蟒蛇

只是为了使球滚动。

import re
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,'forty':40,'fifty':50,'sixty':60,'ninety':90}
modifier = {'hundred':100,'thousand':1000}

while True:
    text = raw_input()
    result = 0
    tmp = 0
    last_multiplier = 1
    for word in re.split('[- ]', text):
        multiplier = modifier.get(word, 1)
        if multiplier > last_multiplier:
            result = (result+tmp)*multiplier
            tmp = 0
        else:
            tmp *= multiplier
        if multiplier != 1:
            last_multiplier = multiplier
        tmp += table.get(word,0)
    print result+tmp

5

Perl + CPAN

为什么要重新发明轮子呢?

use feature 'say';
use Lingua::EN::Words2Nums;

say words2nums $_ while <>;

该程序从标准输入(或从指定为命令行参数的一个或多个文件)中读取英文字符串,每行读取一个英文字符串,然后将相应的数字输出到标准输出中。

我已经使用了来自挑战的示例输入以及通过bsd-games number实用程序(感谢Wander Nauta!)将0到999999的数字转换为文本的详尽测试套件测试了此代码,并且它可以正确解析他们都是。另外,它还理解诸如minus seven(-7),four and twenty(24),four score and seven(87),one gross(144),a baker's dozen(13),eleventy-one(111)和googol(10 100)之类的输入。

注意:除了Perl解释器本身之外,该程序还需要CPAN模块Lingua :: EN :: Words2Nums。这是一些安装CPAN模块的说明。Debian / Ubuntu Linux用户也可以通过APT软件包管理器安装此模块。作为liblingua-en-words2nums-perl。)


4

蟒蛇

具有有效性检查的通用递归解决方案。可以简化所需数字的范围,但这是为了炫耀我猜:

terms = 'zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen'.split()
tee  = 'twenty thirty forty fifty sixty seventy eighty ninety'.split()
for t in tee:
    terms.append(t)
    for s in terms[1:10]:
        terms.append(t+'-'+s)

terms = dict(zip(terms, range(100)))

modifiers = [('hundred', 100), ('thousand', 1000), ('million', 10**6), ('billion', 10**9)]

def read_num(words):
    if len(words) == 0: return 0
    elif len(words) == 1:
        if words[0] in terms:
            return terms[words[0]]
        else:
            raise ValueError(words[0]+' is not a valid english number.')
    else:
        for word, value in reversed(modifiers):
            if word in words:
                i = words.index(word)
                return read_num(words[:i])*value+read_num(words[i+1:])

    raise ValueError(' '.join(words)+' is not a valid english number.')

while True:
    try:
        print(read_num(input().split()))
    except ValueError as e:
        print(e)

2

VBScript 474

不幸的是,这是一个相当常规的答案...如此常规,以至于@Snack几乎发布了相同的过程,但在我之前。

i=split(REPLACE(REPLACE(inputbox(""),"lve","een"),"tho","k"))
o=split("z on tw th fo fi si se ei ni ten ele")
y=split("red *100) k )*1000 ty *10) een +10)")
z=""
p=0
for t=0 to UBOUND(i)
    s=split(i(t),"-")
    u=ubound(s)
    r=s(0)
    for x=0 to UBOUND(o)    
        IF INSTR(r,o(x)) THEN
            z=z+"+"+CSTR(x)
        END IF
        IF u Then
            IF INSTR(s(1),o(x)) THEN
                z=z+CSTR(x)
            END IF
        END IF
    next
    for m=0 to UBOUND(y)
        IF INSTR(r,y(m))AND u=0 THEN
            z=z+y(m+1)
            p=p+1
        END IF
    next
next
Execute("MSGBOX "+String(p,"(")+z)

1

哈斯克尔

我猜想与其他递归解决方案类似,但是我花了一些时间使其变得干净。

这是带有所有说明的完整资源:http : //ideone.com/fc8zcB

-- Define a type for a parser from a list of tokens to the value they represent.
type NParse = [Token] -> Int    

-- Map of literal tokens (0-9, 11-19 and tens) to their names.
literals = [
        ("zero", 0), ("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5), ("six", 6), ("seven", 7), ("eight", 8), ("nine", 9),
        ("eleven", 11), ("twelve", 12), ("thirteen", 13), ("fourteen", 14), ("fifteen", 15), ("sixteen", 16), ("seventeen", 17), ("eighteen", 18), ("nineteen", 19),
        ("ten", 10), ("twenty", 20), ("thirty", 30), ("fourty", 40), ("fifty", 50), ("sixty", 60), ("seventy", 70), ("eighty", 80), ("ninety", 90)
    ]

-- Splits the input string into tokens.
-- We do one special transformation: replace dshes by a new token. Such that "fifty-three" becomes "fifty tens three". 
prepare :: String -> [Token]

-- Let's do the easy stuff and just parse literals first. We just have to look them up in the literals map.
-- This is our base parser.
parseL :: NParse
parseL [tok] = case lookup tok literals of 
    Just x -> x

-- We're going to exploit the fact that the input strings have a tree-like structure like so
--                    thousand
--          hundred             hundred
--      ten       ten       ten         ten
--    lit   lit lit  lit   lit  lit    lit  lit
-- And recursively parse that tree until we only have literal values.
--
-- When parsing the tree
--       thousand
--     h1       h2
-- The resulting value is 1000 * h1 + h2.
-- And this works similarly for all levels of the tree.
-- So instead of writing specific parsers for all levels, let's just write a generic one :

{- genParse :: 
    NParse      : the sub parser
    -> Int      : the left part multiplier
    -> Token    : the boundary token 
    -> NParse   : returns a new parser -}   
genParse :: NParse -> Int -> Token -> NParse    
genParse delegate mul tok = newParser where
    newParser [] = 0
    newParser str = case splitAround str tok of
        -- Split around the boundary token, sub-parse the left and right parts, and combine them
        (l,r) -> (delegate l) * mul + (delegate r)  

-- And so here's the result: 
parseNumber :: String -> Int
parseNumber = parseM . prepare
    where   -- Here are all intermediary parsers for each level
    parseT = genParse   parseL  1       "tens"       -- multiplier is irregular, because the fifty in fifty-three is already multiplied by 10
    parseH = genParse   parseT  100     "hundred"
    parseK = genParse   parseH  1000    "thousand"
    parseM = genParse   parseK  1000000 "million" -- For fun :D

test = (parseNumber "five hundred twenty-three thousand six hundred twelve million two thousand one") == 523612002001

0

普通Lisp,94岁

(write(cdr(assoc(read-line)(loop for i to 999999 collect(cons(format()"~r"i)i)):test #'equalp)))

数字到文本的转换内置到CL中,但反之则不行。为数字建立反向映射并检查其输入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.