У меня есть серия текста, содержащая смешанные числа (например, целая часть и дробная часть). Проблема в том, что текст полон небрежности, закодированной человеком:
Мне нужно регулярное выражение, которое может анализировать эти элементы, чтобы я мог создать правильное число из этого беспорядка.
Для какого языка и / или механизма регулярных выражений он предназначен?
Это было для Java, но с RegexBuddy я мог бы легко установить его на любое количество движков.





Вот регулярное выражение, которое будет обрабатывать все данные, которые я могу ему передать:
(\d++(?! */))? *-? *(?:(\d+) */ *(\d+))?.*$
Это поместит цифры в следующие группы:
Кроме того, вот объяснение элементов в RegexBuddy (которое мне очень помогло при его создании):
Match the regular expression below and capture its match into backreference number 1 «(\d++(?! */))?»
Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
Match a single digit 0..9 «\d++»
Between one and unlimited times, as many times as possible, without giving back (possessive) «++»
Assert that it is impossible to match the regex below starting at this position (negative lookahead) «(?! */)»
Match the character “ ” literally « *»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the character “/” literally «/»
Match the character “ ” literally « *»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the character “-” literally «-?»
Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
Match the character “ ” literally « *»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the regular expression below «(?:(\d+) */ *(\d+))?»
Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
Match the regular expression below and capture its match into backreference number 2 «(\d+)»
Match a single digit 0..9 «\d+»
Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match the character “ ” literally « *»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the character “/” literally «/»
Match the character “ ” literally « *»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the regular expression below and capture its match into backreference number 3 «(\d+)»
Match a single digit 0..9 «\d+»
Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match any single character that is not a line break character «.*»
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Assert position at the end of the string (or before the line break at the end of the string, if any) «$»
деталь .*$ бессмысленна, она просто выбрасывает то, что ей соответствует. Просто удали его. В остальном это не так уж плохо.
Я искал похожее решение, и это то, что мне понравилось: regexlib.com/REDetails.aspx?regexp_id=2127
Я думаю, что может быть проще рассматривать разные случаи (полностью смешанные, только дробные, только числовые) отдельно друг от друга. Например:
sub parse_mixed {
my($mixed) = @_;
if ($mixed =~ /^ *(\d+)[- ]+(\d+) */ *(\d)+(\D.*)?$/) {
return $1+$2/$3;
} elsif ($mixed =~ /^ *(\d+) */ *(\d+)(\D.*)?$/) {
return $1/$2;
} elsif ($mixed =~ /^ *(\d+)(\D.*)?$/) {
return $1;
}
}
print parse_mixed("10"), "\n";
print parse_mixed("1/3"), "\n";
print parse_mixed("1 / 3"), "\n";
print parse_mixed("10 1/3"), "\n";
print parse_mixed("10-1/3"), "\n";
print parse_mixed("10 - 1/3"), "\n";
Если вы используете Perl 5.10, я бы написал его так.
m{
^
\s* # skip leading spaces
(?'whole'
\d++
(?! \s*[/] ) # there should not be a slash immediately following a whole number
)
\s*
(?: # the rest should fail or succeed as a group
-? # ignore possible neg sign
\s*
(?'numerator'
\d+
)
\s*
[/]
\s*
(?'denominator'
\d+
)
)?
}x
Затем вы можете получить доступ к значениям из переменной %+ следующим образом:
$+{whole};
$+{numerator};
$+{denominator};
У меня уже есть регулярное выражение решения, и оно работает очень хорошо, поэтому я собираюсь поделиться им с SO в надежде, что это сэкономит кому-то еще много работы.