Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Регулярные выражения > Не учитывать совпадение при определенных условиях


Автор: Akella 25.5.2012, 09:15
Есть выражение для поиска номеров телефонов:
Код
(\d[\d-•() ]{6,17})|(/\d{3,3}/[\d-•() ]{6,17})


но в тексте часто встречается не только номер телефона, но и цена, или какой-нибудь номер, например, номер объявления
Цитата
м Голосеевская 3-7 мин., 64423457, дата: 25.05.2012

Цитата
тип: Монолитный, цена 154500 у.е. продам


Вопрос. Как построить выражение, которое будет пропускать такие вот совпадения, если в совпавшем тексте найден нежелательный(е) символ(ы)?

Автор: Pfailed 25.5.2012, 13:26
Например так: http://forum.vingrad.ru/index.php?showtopic=339840&view=findpost&p=2413918

Автор: Akella 25.5.2012, 14:03
не доходит, а можно конкретнее?

Автор: Pfailed 25.5.2012, 17:59
Например здесь мы ищем цифры. Но если перед цифрами знак номера, то такие цифры нам не подходят.
На выходе вторая match группа содержит 1234.
Код

'abc № 4444444 fff 1234' =~ /(?:(№\s*)|\b)(\d+)(*SKIP)(?(1)(*FAIL))/ or die;


Словами это выражение: 
(
  1-я альтернатива - найти знак номера, затем возможно пробелы
  2-я альтернатива просто разделитель отделяющий цифры
)
затем цифры, которые мы ищем
просим движок рег выражений не возвращаться на позицию до текущей если поиск потерпит неудачу
говорим что поиск неудачен если найдена 1-я альтернатива

Таким образом:
движок находит "№ 4444444" - 1-я альтернатива
получает указание что поиск неудачен - FAIL
возвратиться за 4444444 он уже не может - SKIP
ищет далее и находит уже 2-ю альтернативу "1234" (действительно тут нет знака номера)
2-я альтернатива - это не первая -> поиск успешен

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

Автор: alezzz 25.5.2012, 20:48
а возле номера телефона есть за что зацепться, например "тел." или код города в скобках?
не в тему, недочитал и непонял суть вопроса.

Автор: Akella 26.5.2012, 19:15
Цитата(alezzz @  25.5.2012,  20:48 Найти цитируемый пост)
а возле номера телефона есть за что зацепться, например "тел." или код города в скобках?


к сожалению часто бывает так, что нет никаких других признаков, того же признака тел не бывает иной раз :(

Автор: Akella 26.5.2012, 19:45
а как насчет просмотра вперед или назад
http://ru2.php.net/manual/ru/regexp.reference.assertions.php

(?<=bullock|donkey)

Автор: Pfailed 26.5.2012, 21:17
Просмотр вперед годится, но просмотр назад в Perl должен быть фиксированой длинны, т.е никаких "*" или "+". Хотя в некоторых других языках, .Net например, такого ограничения нет.

Автор: Akella 21.6.2012, 13:33
Не понял, что не так.
Вот выражение
Код
(?!№)\d[\d-•() ]{6,17}|(?!№)/\d{3,3}/[\d-•() ]{6,17}


Вот текст:
Цитата
Смирнова-Ласточкина, . ТЛФ: +380-67-8763116,  цена: 7 000 грн.,  (067)8763116P.S. Возможна сдачи квартиры под EURO-2012 Call., №68651705, дата: 20.06.2012


Выражение находит все три группы цифр, в том числе и 68651705, хотя это нужно пропускать

Что не так в этом выражении?

Автор: DurRandir 21.6.2012, 16:05
Много почему))

1. Потому что ?! - это look-ahead assertion. А look-behind - это ?<!
2. Заменили, отлично. Но теперь матчинг для выражения №1234567 будет начинаться не с позиции №-->1<--2345, а с позиции №1-->2<--345, т.е. просто пропускает 1ю цифру. Поэтому добавим ещё одно условие - ?<!\d

Итого, получается что-то вроде (+я свернул общий хвост у выражений)
Код

 (?<!№)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}

Автор: Akella 21.6.2012, 16:14
Цитата(DurRandir @  21.6.2012,  16:05 Найти цитируемый пост)
(?<!№)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


всё равно находит

Автор: DurRandir 21.6.2012, 18:11
Код

print "$_\n" for ('abc: +380-67-8763116, fsdfds, (067)8763116P.S. EURO-2012 Call., #68651705, dare 20.06.2012' =~ /(?<!#)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}/g);

380-67-8763116
067)8763116

Я проверял на приведённой строке. Если какие-то другие данные - покажите.

Автор: Akella 22.6.2012, 08:49
Цитата(DurRandir @  21.6.2012,  18:11 Найти цитируемый пост)
#68651705


у меня №, а не #

Добавлено через 48 секунд
Цитата(DurRandir @  21.6.2012,  16:05 Найти цитируемый пост)
Но теперь матчинг для выражения №1234567 будет начинаться не с позиции №-->1<--2345, а с позиции №1-->2<--345, т.е. просто пропускает 1ю цифру.

да, это я уже ещё вчера понял :(

Автор: Akella 22.6.2012, 09:08
Цитата(DurRandir @  21.6.2012,  18:11 Найти цитируемый пост)
print "$_\n" for ('abc: +380-67-8763116, fsdfds, (067)8763116P.S. EURO-2012 Call., #68651705, dare 20.06.2012' =~ /(?<!#)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}/g);


Что-то у Вас тут напутано со скобками, как мне кажется

Добавлено через 1 минуту и 45 секунд
Вот это выражение вообще ничего ненаходит:
Код
(?<!#)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}/g


Добавлено через 2 минуты и 25 секунд
а вот это работает: 
Код

(?<!#)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


Добавлено через 3 минуты и 35 секунд
Но всё равно находит: 

user posted image

Добавлено через 3 минуты и 47 секунд
Может дело в знаке ?

Добавлено через 4 минуты и 38 секунд
Да, действительно, если изменить в тексте объявления № на # и в выражении, то всё ок

Автор: DurRandir 22.6.2012, 14:14
>Вот это выражение вообще ничего ненаходит
А вы не в перле смотрите, судя по скриншоту. Там концовка - /g - модификатор поиска всех совпадений, и требует стартового слеша / - они играют роль скобок-ограничителей.

>всё равно находит
Возможно, проблема в юникоде/кодировках? Я потому и проверял на ASCII-строке, чтобы это не влияло.

Автор: Akella 22.6.2012, 14:39
ну да, наверное, у буржуев ведь нет знака №

Добавлено через 11 секунд
и программа не юникодная

Добавлено через 39 секунд
в общем я перешёл на #, пока что

Автор: Akella 12.4.2013, 14:52
Хочу поднять тему, т.к. есть ещё проблема.
В тексте объявлений есть телефоны.
А также есть вот такой текст:  решения соответствуют требованиями СНиП 31-01-2003 Здания 

Получается так, что выделенная группа цифр определяется, как номер телефона.
Можно что-то придумать такое, что если перед номером есть слово: "СНиП ", то игнорировать найденный текст?

Автор: Pfailed 12.4.2013, 15:36
Как была решена предыдущая проблема?

Автор: Akella 12.4.2013, 16:21
Цитата(Akella @  22.6.2012,  09:08 Найти цитируемый пост)
а вот это работает: 
Выделить всёБез подсветки
1:
    
(?<!#)(?<!\d)(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


ну с одним символом проблемы нет, а как задать конкретное слово? Например, "СНиП "

Автор: Pfailed 12.4.2013, 17:18
На мой взгляд некорректное выражение. Удовлетворяет строке: решения соответствуют требованиями #01-01-2003 Здания.
А должно ли?

Автор: DurRandir 12.4.2013, 18:16
Я бы чуть по другому сделал. Т.к. перед датой может не только "СНИП" идти (и придётся составлять список слов-исключений - регулярка расползётся быстро), я бы вырезал \d{2}-\d{2}-\d{4} как невалидный формат (никто так телефон не делит). Т.е. либо задал для хвоста [\d-() ]{6,17} более строгий формат, либо добавил соответствующий assertion: (?!\d{2,2}-\d{2,2}-\d{4,4}). Итого:

Код

(?<!#)(?<!\d)(?!\d{2}-\d{2}-\d{4})(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


*добавление* а оно же начнёт матчиться после -01-2003
В общем, лучше уточнить валидный формат или делать доп. проверку *после* поиска и выкидывать такие совпадения. 

Т.к. я не уверен, что даже нечто в духе 

Код

(?<![-#\d])(?!\d{2}-\d{2}-\d{4})(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


не зацепит лишнего

Автор: Pfailed 12.4.2013, 18:24
А в чём фишка писать \d{n,n}? Разве это не эквивалентно \d{n}?

Добавлено через 6 минут и 10 секунд
Цитата(DurRandir @  12.4.2013,  18:16 Найти цитируемый пост)
не зацепит лишнего

Но и не отработает на валидной (по условию) строке: решения соответствуют требованиями -01-01-2003 Здания

Автор: Pfailed 12.4.2013, 18:40
Вариация на тему моего первого поста в этой теме
Код

/((?:#|СНиП)\s*(?:\d|\/\d{3,3}\/)[\d-() ]{6,17})|(\b(?:\d|\/\d{3,3}\/)[\d-() ]{6,17})/

Уже в коде смотрим на вторую match группу. Если она определена -- берем ее значение. Если не определена -- переходим на следующую итерацию.

Автор: DurRandir 12.4.2013, 18:41
Цитата
А в чём фишка писать \d{n,n}? Разве это не эквивалентно \d{n}?

Да я не везде исправил. Эквивалентно)

Цитата
Но и не отработает на валидной (по условию) строке: решения соответствуют требованиями -01-01-2003 Здания 

"Если у вас есть проблема, и вы собираетесь решать ее с использованием регулярных выражений, то у вас есть две проблемы" в полный рост, в общем то) Можно только гадать, подойдёт или не подойдёт под реальные данные, которые есть у Akella.

Автор: Akella 13.4.2013, 12:36
Цитата(Pfailed @ 12.4.2013,  18:40)
Вариация на тему моего первого поста в этой теме
Код

/((?:#|СНиП)\s*(?:\d|\/\d{3,3}\/)[\d-() ]{6,17})|(\b(?:\d|\/\d{3,3}\/)[\d-() ]{6,17})/

Уже в коде смотрим на вторую match группу. Если она определена -- берем ее значение. Если не определена -- переходим на следующую итерацию.

Не то.

user posted image

а НЕ должно находить

Добавлено через 9 минут и 49 секунд
Цитата(DurRandir @  12.4.2013,  18:16 Найти цитируемый пост)
(?<![-#\d])(?!\d{2}-\d{2}-\d{4})(?:\d|\/\d{3,3}\/)[\d-() ]{6,17}


а вот это уже норм  smile

Добавлено через 10 минут и 8 секунд
Можно его словесно расшифровать?

Автор: Pfailed 13.4.2013, 15:02
Цитата(Akella @  13.4.2013,  12:36 Найти цитируемый пост)
а НЕ должно находить

В какой match группе нашлось? Я же написал вам, что нужно смотреть только на вторую match группу.
Все исключения в первую match группу, все подходящие во вторую.

Автор: DurRandir 14.4.2013, 21:31
Цитата
Можно его словесно расшифровать? 

Я в том посте постарался описать, как конструировать. Оно состоит из 3х частей - двух отрицательных якорей:

(?<!) - не начинать матчинг, если до позиции стоит -#\d
(?!) - если строка дальше попадает под формат d{2}-\d{2}-\d{4}, то так же не начинать матчинг

и потом положительная проверка под подходящий формат

Автор: Akella 15.4.2013, 09:01
Цитата(Pfailed @  13.4.2013,  15:02 Найти цитируемый пост)
В какой match группе нашлось? Я же написал вам, что нужно смотреть только на вторую match группу.


К сожалению, "смотреть" только туда или только сюда - не подходит, потому что в одном случае будет это выражение, а в другом случае будет другое выражение, но программа одна на всех, как говорится. Можно менять только образец (выражение поиска).

Автор: tishaishii 14.9.2018, 20:36
Создавал такое рег.выр. импирически около 3х лет. Задача была вынимать объявления из отсканированных и распознанных объявлений, преобразованных в HTML. И нужно было искать номера телефонов в БД, чтобы показывать, обработан ли податчик объявления.

В общем, импирически, рег.выр. хранился в отдельном файле около 2х кб + дополнительный анализ подстрок.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)