我正在尝试为识别 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正则表达式引擎,但任何正则表达式都是可以接受的。
答案 0 :(得分:8)
请参阅Perl的Lingua::EN::Words2Nums和Lingua::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;
您可以使用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)
正则表达式确实是一种糟糕的方法。就个人而言,我只是创建一个包含所有已知单词的小地图,并以这种方式进行搜索。 (搜索每个单词,当您找到匹配项时,确定它旁边的单词是否匹配,并继续直到您拥有该数字)。