【问题标题】:Scalable Regex for English Numerals英语数字的可扩展正则表达式
【发布时间】:2009-08-13 03:12:09
【问题描述】:

我正在尝试创建一个正则表达式来识别 English numerals,例如onenineteen20 strong>、122等等,一直到数百万。我想重用正则表达式的某些部分,所以正则表达式是由部分构成的,如下所示:

// 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 正则表达式引擎,但任何正则表达式风格都是可以接受的。

【问题讨论】:

  • 你已经错过了TWO_DIGITS 中的三十个。
  • 还有,是四十,不是四十(别问我为什么,我高中英语差点没通过)
  • 是的,你在这两个方面都是对的,谢谢。我实际上在代码中修复了第一个错误;下次,我应该复制和粘贴:P.

标签: java regex perl


【解决方案1】:

参见 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

恐怕这之后会变得更难;-)

【讨论】:

  • 不,恰恰相反。正则表达式用于查找英语中的数字(包括诸如“fourscore 和七”之类的短语并将它们转换为数字。Lingua::EN::FindNumber 中的正则表达式通过分解常见的前缀和后缀进行了优化。funky obscure行为找到的每个数字短语调用 words2nums,重新插入被 words2nums 吞噬的任何尾随空格。
  • 是的,我在写评论时发现了这一点,并在第二句话中进行了解释。 “Lingua::EN::FindNumber 中的正则表达式通过分解出常见的前缀和后缀进行了优化。”非常感谢您的解释;现在它实际上是有道理的:)。您是否知道这是手动创建的还是自动生成的?另外,我不想将单词转换为数字,只想识别它们。
  • 如果有一个正则表达式要求使用“x”修饰符进行处理,就是这样。
【解决方案2】:

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。

【讨论】:

    【解决方案3】:

    你想做什么,你使用什么算法?

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

    一百万一十万一百一

    你可以有一个正则表达式来识别百万、它之前的任何东西(即“一”)以及它之后的所有东西(即“十万一百一”)。之前的内容将被捕获以进行额外匹配(使用您的数百、十和个正则表达式),而之后的内容将被捕获以进行额外匹配(使用您的数千个正则表达式、数百个正则表达式、十个正则表达式或一个正则表达式)。

    这个算法自然是递归的,实现起来不会太难。在不知道您要完成的具体内容或 Java 正则表达式引擎的详细信息的情况下,我无法提供更多建议。

    【讨论】:

    • 我只是想在文本中识别英文数字。例如,在“世界上有 60 亿人,加上大约 7.77 亿人。”这句话中,我想识别“六十亿”和“七亿七千七百万”。
    【解决方案4】:

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

    【讨论】:

    • 你最终会做与正则表达式引擎几乎相同的事情,除非你使用像 Aho-Corasick 的算法这样的东西
    猜你喜欢
    • 2016-12-02
    • 1970-01-01
    • 2018-11-07
    • 1970-01-01
    • 2016-09-26
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多