Регулярное выражение для соответствия фракциям, а не датам - PullRequest
4 голосов
/ 16 декабря 2009

Я пытаюсь найти регулярное выражение, которое будет соответствовать дроби (1/2), но не дате (5/5/2005) в строке. Любая помощь вообще была бы великолепна, все, что я смог придумать, это (\ d +) / (\ d +), который находит совпадения в обеих строках. Заранее спасибо за помощь.

Ответы [ 6 ]

9 голосов
/ 16 декабря 2009

Предполагая PCRE, используйте отрицательный взгляд вперед и взгляд назад:

(?<![\/\d])(\d+)\/(\d+)(?![\/\d])

Взгляд (группа (?=)) говорит: «Сопоставьте это с вещами, если за ними следуют другие». Содержимое lookahead не совпадает. Мы отрицаем это (группа (?!)) так, чтобы это не соответствует материалу после нашей дроби - таким образом, мы не сопоставляем группу в следующем.

Дополнением к lookahead является lookbehind (группа (?<=)) делает противоположное - оно сопоставляет вещи, если им предшествует этот другой материал, и точно так же, как lookahead, мы можем отрицать это (группа (?<!)) чтобы мы могли сопоставлять вещи, которые не следуют чему-то.

Вместе они гарантируют, что наша фракция не будет иметь других частей фракций до или после нее. Он не предъявляет никаких других произвольных требований к входным данным. Он будет соответствовать дроби 2/3 в строке "te2/3xt", в отличие от большинства других предоставленных примеров.

Если ваш тип регулярного выражения использует // s для разграничения регулярных выражений, вам придется избегать косых черт в этом или использовать другой разделитель (Perl m{} будет хорошим выбором здесь).


Редактировать: По-видимому, ни один из этих регулярных выражений не работает, потому что механизм регулярных выражений откатывает назад и сопоставляет меньшее число для удовлетворения требований регулярного выражения. Когда я так долго работал над одним регулярным выражением, я бездействую и решаю, что, возможно, одно гигантское регулярное выражение не является ответом, и я пишу функцию, которая использует регулярное выражение и несколько других инструментов, чтобы сделать это для меня. Вы сказали, что используете Ruby. Это работает для меня:

>> def get_fraction(s)
>>   if s =~ /(\d+)\/(\d+)(\/\d+)?/
>>     if $3 == nil
>>       return $1, $2
>>     end
>>   end
>>   return nil
>> end
=> nil
>> get_fraction("1/2")
=> ["1", "2"]
>> get_fraction("1/2/3")
=> nil

Эта функция возвращает две части дроби, но возвращает nil, если это дата (или если дроби нет). Это не подходит для "1/2/3 and 4/5", но я не знаю, хотите ли вы (или нужно), чтобы это прошло. В любом случае, я рекомендую, чтобы в будущем, когда вы спросите в Stack Overflow: «Как мне сделать регулярное выражение, чтобы соответствовать этому?» сначала вы должны сделать шаг назад и посмотреть, сможете ли вы сделать это, используя регулярное выражение и немного больше. Регулярные выражения являются отличным инструментом и могут многое, но их не всегда нужно использовать в одиночку.


РЕДАКТИРОВАТЬ 2:

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

РЕДАКТИРОВАТЬ 3:

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

6 голосов
/ 16 декабря 2009

Используйте отрицательный взгляд вперед и взгляд назад .

/(?<![\/\d])(?:\d+)\/(?:\d+)(?![\/\d])/

РЕДАКТИРОВАТЬ: я исправил свой ответ на ловушку для ошибки возврата, выявленной @j_random_hacker. В качестве доказательства я предлагаю следующий быстрый и грязный скрипт php:

<?php
$subject = "The match should include 1/2 but not 12/34/56 but 11/23, now that's ok.";
$matches = array();
preg_match_all('/(?<![\/\d])(?:\d+)\/(?:\d+)(?![\/\d])/', $subject, $matches);
var_dump($matches);
?>

который выводит:

array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(3) "1/2"
    [1]=>
    string(5) "11/23"
  }
}
4 голосов
/ 16 декабря 2009

Lookahead отлично подходит, если вы используете Perl или PCRE, но если они недоступны в используемом вами движке регулярных выражений, вы можете использовать:

(^|[^/\d])(\d+)/(\d+)($|[^/\d])

2-й и 3-й захваченные сегменты будут числителем и знаменателем.

Если вы делаете , используйте вышеперечисленное в регулярном выражении Perl, не забудьте экранировать / s - или использовать другой разделитель, например ::

m!(?:^|[^/])(\d+)/(\d+)(?:$|[^/])!

В этом случае вы можете использовать (?:...), чтобы избежать сохранения неинтересных частей в скобках.

РЕДАКТИРОВАТЬ 18/12/2009 : Крис Латс заметил хитрая ошибка, вызванная возвратом, которая мучает большинство из этих ответов - я считаю, что это теперь исправлено в моем.

0 голосов
/ 16 декабря 2009

В зависимости от языка, с которым вы работаете, вы можете попробовать утверждения с отрицательным прогнозом или прогнозом: в perl (?! Pattern) утверждается, что / pattern / не может следовать за совпадающей строкой.

Или, опять же, в зависимости от языка и всего, что вы знаете о контексте, может быть уместным совпадение границы слова (\ b в perl).

0 голосов
/ 16 декабря 2009

это будет работать: (?<![/]{1})\d+/\d+(?![/]{1})

0 голосов
/ 16 декабря 2009

если его линейный вход можно попробовать

^(\d+)\/(\d+)$

в противном случае используйте это возможно

^(\d+)\/(\d+)[^\\]*.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...