2008-10-29 2 views
11

У меня есть текст, содержащий смешанные числа (т. Е. Целую часть и дробную часть). Проблема заключается в том, что текст полон человеческой кодировки разгильдяйства:Регулярное совпадение с неаккуратными фракциями/смешанными номерами

  1. целая часть может или не может существовать (например: «10»)
  2. дробная часть может существовать или не существовать (например: " 1/3 ")
  3. Две части могут быть разделены пробелами и/или дефисами (например:« 10 1/3 »,« 10-1/3 »,« 10 - 1/3 »).
  4. Сама фракция может иметь или не иметь пробелов между числом и косой чертой (например: «1/3», «1/3», «1/3»).
  5. Там может быть другой текст, после того, как фракция, которая должна быть проигнорировано

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

+0

Я уже получил регулярное выражение решения, и она работает очень хорошо, поэтому я буду делить его с ТАК в надежде, что это спасет кого-то еще много работы. – 2008-10-29 00:14:14

+0

На каком языке и/или регулярном двигателе это нужно? – 2008-10-30 04:21:33

ответ

10

Вот регулярное выражение, которое будет обрабатывать все данные, которые я могу бросить на нее:

(\d++(?! */))? *-? *(?:(\d+) */ *(\d+))?.*$ 

Это позволит поставить цифры на следующие группы:

  1. целая часть смешанного числа , если она существует
  2. числителя, если фракция выходит
  3. знаменателя, если фракция существует

Кроме того, вот 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) «$» 
2

Я думаю, что это может быть легче решать различные случаи (полная смешанная, фракция, единственное число) отдельно друг от друга. Например:

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

Если вы используете 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}; 
Смежные вопросы