英文数字的可扩展正则表达式

时间:2009-08-13 03:12:10

标签: java regex perl

我正在尝试为识别 English numerals创建一个正则表达式,例如一个十九二十一百二十二等等,一直到数百万。我想重用正则表达式的某些部分,因此正则表达式是由部分构造的,如下所示:

// replace <TAG> with the content of the variable
ONE_DIGIT = (?:one|two|three|four|five|six|seven|eight|nine)
TEEN = (?:ten|eleven|twelve|(?:thir|for|fif|six|seven|eigh|nine)teen)
TWO_DIGITS = (?:(?:twen|thir|for|fif|six|seven|eigh|nine)ty(?:\s+<ONE_DIGIT>)?|<TEEN>)
// HUNDREDS, et cetera

我想知道是否有人已经做了同样的事情(并希望分享),因为这些正则表达式很长,并且他们可能有他们不应该做的事情,或者我可能会遗漏的事情。另外,我希望它们尽可能高效,所以我期待任何优化提示。我正在使用Java正则表达式引擎,但任何正则表达式都是可以接受的。

4 个答案:

答案 0 :(得分:8)

请参阅Perl的Lingua::EN::Words2NumsLingua::EN::FindNumber

特别是source code for Lingua::EN::FindNumber包含:

# This is from Lingua::EN::Words2Nums, after being thrown through
# Regex::PreSuf
my $numbers =
    qr/((?:b(?:akers?dozen|illi(?:ard|on))|centillion|d(?:ecilli(?:ard|on)|ozen|u(?:o(?:decilli(?:ard|on)|vigintillion)|vigintillion))|e(?:ight(?:een|ieth|[yh])?|leven(?:ty(?:first|one))?|s)|f(?:i(?:ft(?:een|ieth|[yh])|rst|ve)|o(?:rt(?:ieth|y)|ur(?:t(?:ieth|[yh]))?))|g(?:oogol(?:plex)?|ross)|hundred|mi(?:l(?:ion|li(?:ard|on))|nus)|n(?:aught|egative|in(?:et(?:ieth|y)|t(?:een|[yh])|e)|o(?:nilli(?:ard|on)|ught|vem(?:dec|vigint)illion))|o(?:ct(?:illi(?:ard|on)|o(?:dec|vigint)illion)|ne)|qu(?:a(?:drilli(?:ard|on)|ttuor(?:decilli(?:ard|on)|vigintillion))|in(?:decilli(?:ard|on)|tilli(?:ard|on)|vigintillion))|s(?:core|e(?:cond|pt(?:en(?:dec|vigint)illion|illi(?:ard|on))|ven(?:t(?:ieth|y))?|x(?:decillion|tilli(?:ard|on)|vigintillion))|ix(?:t(?:ieth|y))?)|t(?:ee?n|h(?:ir(?:t(?:een|ieth|y)|d)|ousand|ree)|r(?:e(?:decilli(?:ard|on)|vigintillion)|i(?:gintillion|lli(?:ard|on)))|w(?:e(?:l(?:fth|ve)|nt(?:ieth|y))|o)|h)|un(?:decilli(?:ard|on)|vigintillion)|vigintillion|zero|s))/i;

Perl's Artistic License的约束。

您可以使用Regex::PreSuf自动分解常见的前缀和后缀:

#!/usr/bin/perl

use strict;
use warnings;

use Regex::PreSuf;

my %singledigit = (
    one    => 1,
    two    => 2,
    three  => 3,
    four   => 4,
    five   => 5,
    six    => 6,
    seven  => 7,
    eight  => 8,
    nine   => 9,
);

my $singledigit = presuf(keys %singledigit);

print $singledigit, "\n";

my $text = "one two three four five six seven eight nine";

$text =~ s/($singledigit)/$singledigit{$1}/g;

print $text, "\n";

<强>输出:

C:\Temp> cvb
(?:eight|f(?:ive|our)|nine|one|s(?:even|ix)|t(?:hree|wo))
1 2 3 4 5 6 7 8 9

我担心这会变得更难; - )

答案 1 :(得分:3)

Perl有许多模块可以使用不同的技术生成优化的正则表达式(大多数只使用标准功能,因此应该可以在Java中使用)。您可以在http://groups.google.com/group/perl.perl5.porters/msg/132877aee7542015中看到Regexp :: Assemble,Regexp :: List,Regexp :: Optimizer和Regex :: PreSuf输出的示例。从perl 5.10开始,perl本身通常会将|'完全字符串列表优化为trie。

答案 2 :(得分:0)

你想做什么以及你使用的算法是什么?

我不熟悉Java正则表达式引擎,但我使用的其他正则表达式引擎(Perl,awk)允许您捕获匹配。例如,如果您要匹配:

百万十万一百一百。

你可以有一个正则表达式,可以识别百万,它之前的任何东西(即'一'),以及它之后的一切(即'十万一百')。之前的事情将被捕获以进行额外的匹配(使用你的数百,数十和一个正则表达式),之后的东西将被捕获以进行额外的匹配(使用你的千位正则表达式,数百种正则表达式,数十种正则表达式或正则表达式)。

此算法自然是递归的,并且实现起来不会太难。在不知道你想要完成什么的细节或者Java的正则表达式引擎的细节的情况下,我无法提出更多建议。

答案 3 :(得分:0)

正则表达式确实是一种糟糕的方法。就个人而言,我只是创建一个包含所有已知单词的小地图,并以这种方式进行搜索。 (搜索每个单词,当您找到匹配项时,确定它旁边的单词是否匹配,并继续直到您拥有该数字)。