Символы соответствия регулярному выражению, если им не предшествует строка - PullRequest
3 голосов
/ 19 января 2020

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

I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you $6. 00 because you bought me a No. 3 burger. -Sgt. Smith

Я использую это с функцией re.split в Python 3 Я хочу получить это:

["I am from New York, N.Y. and I would like to say hello!",
"How are you today?",
"I am well.",
"I owe you $6. 00 because you bought me a No. 3 burger."
"-Sgt. Smith"]

В настоящее время это мое регулярное выражение:

(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)(?<=[^N]..)(?<=[^o].)

Я решил попробовать сначала исправьте No. с последними двумя условиями. Но это зависит от сопоставления N и o независимо, что, я думаю, приведет к ложным срабатываниям в других местах. Я не могу понять, как заставить его сделать только строку No за периодом. Затем я буду использовать аналогичный подход для Sgt. и любых других «проблемных» строк, с которыми я сталкиваюсь.

Я пытаюсь использовать что-то вроде:

(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)^(?<=^No$)

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

Вот регулярное выражение моей ситуации: https://regexr.com/4sgcb

Ответы [ 4 ]

2 голосов
/ 19 января 2020

Как уже упоминалось в моем комментарии выше, если вы не можете определить фиксированный набор крайних случаев, это может быть невозможно без ложных срабатываний или ложных отрицаний. Опять же, без контекста вы не можете определить guish между аббревиатурами, такими как "-Sgt. Smith", и концами предложений, таких как "Sergeant часто сокращается как Sgt. Это делает его короче.".

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


1. Определите ваши крайние случаи

Например, вы можете назначить guish «У меня будет № 3» и «Нет. Я ваш отец», проверив следующий номер. Таким образом, вы бы идентифицировали этот крайний случай с помощью регулярного выражения, подобного этому: No. \d. (Опять же, контекст имеет значение. Такие предложения, как «Достаточно ли 200? Нет, недостаточно 200.» Все равно дадут вам ложный положительный результат)

2. Маскируйте ваши крайние случаи

Для каждого краевого случая маскируйте строку соответствующей строкой, которая на 100% не будет частью исходного текста. Например, "Нет" => "====== НОМЕР ======"

3. Запустите ваш алгоритм

Теперь, когда вы избавились от нежелательных знаков препинания, вы можете выполнить более простое регулярное выражение, подобное этому, чтобы определить истинные положительные моменты: [\.\!\?]\s

4. Снимите маски с краев

Превратите "====== NUMBER ======" в "Нет"

2 голосов
/ 19 января 2020

Это самое близкое регулярное выражение, которое я мог получить (конечный пробел - это то, что мы сопоставляем):

(?<=(?<!(No|\.\w))[\.\?\!])(?! *\d+ *) 

, который также разделится после Sgt. по той простой причине, что утверждение за кадром должно быть фиксированная ширина в Python (что за ограничение!).

Вот как я бы это сделал в vim, который не имеет такого ограничения (конечный пробел - это то, что мы сопоставляем):

\(\(No\|Sgt\|\.\w\)\@<![?.!]\)\( *\d\+ *\)\@!\zs 

Для ОП, а также для обычного читателя, этот вопрос и ответы на него о перспективах и очень интересны.

2 голосов
/ 19 января 2020

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

Используйте шаблон как

\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))

См. regex demo

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

Python demo :

import re
p = re.compile(r'\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))')
s = "I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you $6. 00 because you bought me a No. 3 burger. -Sgt. Smith"
for m in p.findall(s):
    print(m)

Вывод:

I am from New York, N.Y. and I would like to say hello!
How are you today?
I am well.
I owe you $6. 00 because you bought me a No. 3 burger.
-Sgt. Smith

Детали шаблона

  • \s* - соответствует 0 или более пробелов (используется для обрезки результатов)
  • (?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+ - один или более вхождений нескольких альтернатив:
    • \d+\.\s*\d+ - 1+ цифр, ., 0+ пробелов, 1+ цифр
    • (?:No|M[rs]|[JD]r|S(?:r|gt))\. - сокращенные строки, такие как No., Mr., Ms., Jr., Dr., Sr., Sgt.
    • \.(?!\s+-?[A-Z0-9]) - соответствует точке, за которой не следует 1 или более whitespa ce, а затем необязательные - и заглавные буквы или цифры
    • | - или
    • [^.!?] - любой символ, кроме ., ! и ?
  • (?:[.?!]|$) - ., ! и ? или конец строки.
1 голос
/ 19 января 2020

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

Сам я бы сделал это в три этапа:

  1. Заменить пробелы, которые должны остаться с каким-то специальным символом (re.sub)
  2. Разделить текст (re.split)
  3. Заменить специальный символ пробелом

Например :

import re

zero_width_space = '\u200B'

s = 'I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you $6. 00 because you bought me a No. 3 burger. -Sgt. Smith'

s = re.sub(r'(?<=\.)\s+(?=[\da-z])|(?<=,)\s+|(?<=Sgt\.)\s+', zero_width_space, s)
s = re.split(r'(?<=[.?!])\s+', s)

from pprint import pprint
pprint([line.replace(zero_width_space, ' ') for line in s])

Отпечатки:

['I am from New York, N.Y. and I would like to say hello!',
 'How are you today?',
 'I am well.',
 'I owe you $6. 00 because you bought me a No. 3 burger.',
 '-Sgt. Smith']
...