试图简化正则表达式

时间:2009-09-27 23:37:47

标签: regex

我将在周末分析Campaign Finance贡献记录。有趣!

我注意到的一个令人讨厌的事情是实体名称的输入方式不同:

例如,我看到这样的内容:'llc''llc.''l l c''l.l.c''l. l. c.''llc,'等。

我正试图抓住所有这些变种。

所以它会是这样的:

"l([,\.\ ]*)l([,\.\ ]*)c([,\.\ ]*)"

哪个不是那么糟糕......除了我能想到的大约40个实体后缀。

我能想到的最好的事情是根据我的后缀列表以编程方式构建此模式。

我想知道是否有一种更好的方法可以在一个人类可读/可写的正则表达式中处理它。

6 个答案:

答案 0 :(得分:2)

正则表达式(相对简单的除外)和可读性很少是相辅相成的。不要误解我,我喜欢他们通常带来的简单,但他们不适合所有目的。

如果你想要可读性,只需创建一个可能值的数组并迭代它们,检查你的字段,看看是否匹配。

除非您正在进行基因测序,否则速度差异无关紧要。当你发现它时,添加一个新的将更容易。将元素添加到数组基本上比反向工程正则表达式更容易。

答案 1 :(得分:2)

你可以剥掉多余的垃圾。使用Perl:

my $suffix = "l. lc.."; # the worst case imaginable!

$suffix =~ s/[.\s]//g;
# no matter what variation $suffix was, it's now just "llc"

显然,如果您在完整的公司名称上使用它,可能会输入您的输入,但如果过于深入了解如何执行此操作则需要了解我们正在使用的语言。一个可能的正则表达式解决方案是复制公司名称并删除一些常用单词和任何超过(大约)4个字符的单词:

my $suffix = $full_name;

$suffix =~ s/\w{4,}//g; # strip words of more than 4 characters
$suffix =~ s/(a|the|an|of)//ig; # strip a few common cases
# now we can mangle $suffix all we want
# and be relatively sure of what we're doing

它并不完美,但它应该相当有效,并且比使用单个“怪物正则表达式”试图匹配所有它们更具可读性。作为一项规则,不要使用怪物正则表达式来匹配所有情况,使用一系列专门的正则表达式将许多情况缩小到几个。它会更容易理解。

答案 2 :(得分:0)

前两个“l”部分可以通过[the first "l" part here]{2}进行简化。

答案 3 :(得分:0)

在匹配之前,您可以首先压缩句点和空格:例如,在perl中:

while (<>) {
  $Sq = $_;
  $Sq =~ s/[.\s]//g; # squish away . and " " in the temporary save version
  $Sq = lc($Sq);
  /^llc$/ and $_ = 'L.L.C.'; # try to match, if so save the canonical version
  /^ibm/ and $_ = 'IBM'; # a different match
  print $_;
}

答案 4 :(得分:0)

请勿使用正则表达式,而是构建所有已发现(目前为止)条目及其“规范”(收藏)版本的地图。

还可以构建一个工具,通过识别特定数量字符的公共前缀并在屏幕上打印它们来发现可能的后缀新变种,以便添加新规则。

答案 5 :(得分:0)

在Perl中,您可以使用字符串在程序内部构建正则表达式。这是一些示例代码:

#!/usr/bin/perl

use strict;
use warnings;

my @strings = (
    "l.l.c",
    "llc",
    "LLC",
    "lLc",
    "l,l,c",
    "L . L C ",
    "l  W c"
);

my @seps = ('.',',','\s');
my $sep_regex = '[' . join('', @seps) . ']*';
my $regex_def = join '', (
    '[lL]',
    $sep_regex,
    '[lL]',
    $sep_regex,
    '[cC]'
);

print "definition: $regex_def\n";

foreach my $str (@strings) {
    if ( $str =~ /$regex_def/ ) {
        print "$str matches\n";
    } else {
        print "$str doesn't match\n";
    }
}

这个正则表达式也可以通过使用不区分大小写的匹配(这意味着$ match =〜/ $ regex / i)来简化。如果在您定义的字符串上运行此操作几次,则可以轻松查看未根据正则表达式进行验证的情况。以这种方式构建正则表达式对于仅定义一次分隔符符号非常有用,我认为人们可能会使用相同的分隔符来表示各种缩写(如IRS,I.R.S,irs等)。

您也可以考虑研究approximate string matching算法,这些算法在很多领域都很流行。这些背后的想法是您定义一个用于比较字符串的评分系统,然后您可以测量输入字符串与规范字符串的相似程度,以便您可以识别“LLC”和“lLc”是非常相似的字符串。

或者,正如其他人建议您可以编写一个输入清理程序来删除不需要的字符,如空格,逗号和句点。在上述程序的上下文中,您可以这样做:

my $sep_regex = '[' . join('', @seps) . ']*';
foreach my $str (@strings) {
    my $copy = $str;
    $copy =~ s/$sep_regex//g;
$copy = lc $copy;
    print "$str -> $copy\n";
}

如果您可以控制最初输入数据的方式,您可以使用这样的清洁剂来验证来自用户和其他程序的输入,这将使您的分析更加容易。