2009-09-27 2 views
1

Я провожу выходные дни, анализируя записи о взносах в кампанию. Весело!Попытка упростить регулярное выражение

Одна из раздражающих вещей, которые я заметил, что имена сущностей вводятся по-разному:

Например, я вижу вещи, как это: 'llc', 'llc.', 'l l c', 'l.l.c', 'l. l. c.', 'llc,' и т.д.

Я пытаюсь поймать все эти варианты.

Так что было бы что-то вроде:

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

, который не так уж плохо ... кроме того есть около 40 сущностей суффиксов, что я могу думать.

Лучшее, что я могу придумать, это программно создать этот шаблон, основанный на моем списке суффиксов.

Мне интересно, есть ли лучший способ справиться с этим в пределах одного регулярного выражения, которое доступно для чтения/записи.

ответ

0

Первые две детали «l» могут быть упрощены на [the first "l" part here]{2}.

2

Регулярные выражения (кроме относительно простых) и удобочитаемость редко идут рука об руку. Не поймите меня неправильно, я люблю их за простоту, которую они обычно приносят, но они не подходят для всех целей.

Если вам нужна читаемость, просто создайте массив возможных значений и пройдите через них, проверив ваше поле против них, чтобы узнать, есть ли совпадение.

Если вы не выполняете последовательность генов, разница в скорости не должна иметь значения. И при добавлении нового будет намного проще добавить новый. Добавление элемента в массив существенно проще, чем обратное проектирование регулярного выражения.

+1

Вы можете создать массив регулярных выражений, по одному для каждого возможного суффикса, и пройти через них. Это было бы относительно читаемо, но все же немного сложно поддерживать. –

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 $_; 
} 
+0

Если вы собираетесь сопоставлять текст, вы можете просто использовать модификатор '/ i', чтобы игнорировать регистр. Это проще, чем запомнить 'lc()' все ИМХО. –

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 

Это не идеально, но оно должно быть достаточно эффективным и более удобным для чтения чем использовать одно «регулярное выражение монстра», чтобы попытаться сопоставить их все. Как правило, не используйте регулярное выражение монстра для соответствия всем случаям, используйте серию специализированных регулярных выражений, чтобы сократить количество случаев до нескольких. Это будет легче понять.

0

Не используйте регулярные выражения, а создавайте карту всех обнаруженных (пока) записей и их «канонических» (любимых) версий.

Также создайте инструмент для обнаружения возможных новых вариантов постфиксных имен путем определения общих префиксов для определенного количества символов и их печати на экране, чтобы вы могли добавлять новые правила.

+0

Это не имеет смысла. Как бы вы построили эту карту без регулярного выражения? Рукой? Это звучит забавно для поддержания. Я могу понять отвращение к загадочным регулярным выражениям, но это именно то, что нужно было избежать, чтобы избежать кодов кошмаров. –

+0

Я согласен с Крисом. Регулярные выражения представляют собой функцию, описывающую такое отображение в меньшем пространстве. –

+0

есть, вручную. В любом случае люди будут вводить только определенные варианты. Существует также ограниченный набор случаев. То, что он предлагает, - это преобразование или интерпретация входных данных пользователей - ручная проверка этого преобразования более честна, чем доверять удаче. Вы можете оптимизировать его для регулярных выражений позже. –

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"; 
    } 
} 

Это регулярное выражение также может быть упрощен с помощью регистронезависимого соответствия (что означает $ совпадения = ~/$ регулярное выражение/я). Если вы запускаете это несколько раз в строках, которые вы определяете, вы можете легко увидеть случаи, которые не проверяются в соответствии с вашим регулярным выражением. Создание регулярного выражения таким образом может быть полезно только для определения символов разделителя один раз, и я думаю, что люди, вероятно, будут использовать одни и те же разделители для самых разных сокращений (например, 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"; 
} 

Если у вас есть контроль, как данные вводятся первоначально, вы могли бы использовать такие дезинфицирующее для проверки вводимых данных от пользователей и других программ, которые будет сделать ваш анализ намного проще.

+0

Кажется довольно глупым рассматривать использование '[lL]', когда вы могли бы просто использовать '/ i' в конце, чтобы все было лучше. Большинство ароматизаторов регулярных выражений имеют аналогичные возможности для легкого совпадения без учета регистра. –

+0

@Chris - Я пытался сбалансировать переносимость с лаконичностью, но вы определенно правы. Я сделал это в ответ. –