Ответ 1
(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s
Попробуйте это. разделите свою строку this.You также можете проверить демо.
Я хочу составить список предложений из строки, а затем распечатать их. Я не хочу использовать NLTK для этого. Поэтому он должен разбиваться на период в конце предложения, а не на десятичные знаки, аббревиатуры или название имени, или если предложение имеет .com. Это попытка регулярного выражения, которая не работает.
import re
text = """\
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't.
"""
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)
for stuff in sentences:
print(stuff)
Пример вывода того, как он должен выглядеть
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it.
Did he mind?
Adam Jones Jr. thinks he didn't.
In any case, this isn't true...
Well, with a probability of .9 it isn't.
(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s
Попробуйте это. разделите свою строку this.You также можете проверить демо.
Хорошо, поэтому предложения-токенизаторы - это то, на что я смотрел в деталях, используя регулярные выражения, nltk, CoreNLP. Вы в конечном итоге пишете свои собственные, и это зависит от приложения. Этот материал сложный и ценный, и люди не просто отдают свой код токенизатора. (В конечном счете, токенизация не является детерминированной процедурой, она вероятностна и также очень сильно зависит от вашего корпуса или домена, например, сообщения в социальных сетях и обзоры Yelp vs...)
В общем, вы не можете полагаться на одно непогрешимое регулярное выражение Great White, вам нужно написать функцию, которая использует несколько регулярных выражений (как положительных, так и отрицательных); также словарь аббревиатур, а также некоторый базовый анализ языка, который знает, что, например, "I", "США", "FCC", "TARP" капитализируются на английском языке.
Чтобы проиллюстрировать, насколько легко это может серьезно осложниться, попробуйте написать вам эту функциональную спецификацию для детерминированного токенизатора, чтобы решить, указывает ли один или несколько периодов ('.'/'...') конец предложения или что-то еще еще:
function isEndOfSentence(leftContext, rightContext)
В простом (детерминированном) случае function isEndOfSentence(leftContext, rightContext)
возвращает логическое значение, но в более общем смысле оно вероятностно: оно возвращает float 0.0-1.0 (уровень уверенности, что этот конкретный "." - конец предложения),
Ссылки: [a] Видео Coursera: "Обработка основного текста 2-5 - Сегментация предложений" - Стэнфордский НЛП - Профессор Дэн Юрафски и Крис Мэннинг " [UPDATE: неофициальная версия, которая использовалась на YouTube, была снята]
Попробуйте разбить вход в соответствии с пробелами, а не точкой или ?
, если вы сделаете это так, точка или ?
не будут напечатаны в конечном результате.
>>> import re
>>> s = """Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't."""
>>> m = re.split(r'(?<=[^A-Z].[.?]) +(?=[A-Z])', s)
>>> for i in m:
... print i
...
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it.
Did he mind?
Adam Jones Jr. thinks he didn't.
In any case, this isn't true...
Well, with a probability of .9 it isn't.
sent = re.split('(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)(\s|[A-Z].*)',text)
for s in sent:
print s
Здесь используется регулярное выражение: (?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)(\s|[A-Z].*)
Первый блок: (?<!\w\.\w.)
: этот шаблон ищет в контуре с отрицательной обратной связью (?<!)
для всех слов (\w)
, за которым следует fullstop (\.)
, за которым следуют другие слова (\.)
Второй блок: (?<![A-Z][a-z]\.)
: этот шаблон ищет в цикле отрицательной обратной связи для чего-либо, начиная с букв верхнего регистра ([A-Z])
, а затем строчных алфавитов ([A-Z])
до тех пор, пока не будет найдена точка (\.)
.
Третий блок: (?<=\.|\?)
: этот шаблон ищет в петле обратной связи точки (\.)
ИЛИ вопросительный знак (\?)
Четвертый блок: (\s|[A-Z].*)
: этот шаблон ищет после метки точки ИЛИ вопроса из третьего блока. Он ищет пустое пространство (\s)
ИЛИ любую последовательность символов, начиная с алфавита верхнего регистра ([A-Z].*)
.
Этот блок очень важен для разделения, если вход имеет значение
Привет, мир. Привет, я здесь сегодня.
то есть. если после точки есть пробел или пробел.
Попробуйте следующее:
(?<!\b(?:[A-Z][a-z]|\d|[i.e]))\.(?!\b(?:com|\d+)\b)
Наивный подход для правильных английских предложений, не начинающийся с не-альфа и не содержащий цитируемых частей речи:
import re
text = """\
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't.
"""
EndPunctuation = re.compile(r'([\.\?\!]\s+)')
NonEndings = re.compile(r'(?:Mrs?|Jr|i\.e)\.\s*$')
parts = EndPunctuation.split(text)
sentence = []
for part in parts:
if len(part) and len(sentence) and EndPunctuation.match(sentence[-1]) and not NonEndings.search(''.join(sentence)):
print(''.join(sentence))
sentence = []
if len(part):
sentence.append(part)
if len(sentence):
print(''.join(sentence))
Ложное положительное расщепление может быть уменьшено путем расширения NonEndings немного. В других случаях потребуется дополнительный код. При таком подходе обработка опечаток разумным способом окажется сложной.
Вы никогда не достигнете совершенства при таком подходе. Но в зависимости от задачи он может просто работать "достаточно"...
Я написал это с учетом комментариев smci выше. Это подход среднего класса, который не требует внешних библиотек и не использует регулярное выражение. Он позволяет вам предоставить список сокращений и счетов для предложений, заканчивающихся терминаторами в оболочках, таких как период и цитата: [. ",? ',.)].
abbreviations = {'dr.': 'doctor', 'mr.': 'mister', 'bro.': 'brother', 'bro': 'brother', 'mrs.': 'mistress', 'ms.': 'miss', 'jr.': 'junior', 'sr.': 'senior', 'i.e.': 'for example', 'e.g.': 'for example', 'vs.': 'versus'}
terminators = ['.', '!', '?']
wrappers = ['"', "'", ')', ']', '}']
def find_sentences(paragraph):
end = True
sentences = []
while end > -1:
end = find_sentence_end(paragraph)
if end > -1:
sentences.append(paragraph[end:].strip())
paragraph = paragraph[:end]
sentences.append(paragraph)
sentences.reverse()
return sentences
def find_sentence_end(paragraph):
[possible_endings, contraction_locations] = [[], []]
contractions = abbreviations.keys()
sentence_terminators = terminators + [terminator + wrapper for wrapper in wrappers for terminator in terminators]
for sentence_terminator in sentence_terminators:
t_indices = list(find_all(paragraph, sentence_terminator))
possible_endings.extend(([] if not len(t_indices) else [[i, len(sentence_terminator)] for i in t_indices]))
for contraction in contractions:
c_indices = list(find_all(paragraph, contraction))
contraction_locations.extend(([] if not len(c_indices) else [i + len(contraction) for i in c_indices]))
possible_endings = [pe for pe in possible_endings if pe[0] + pe[1] not in contraction_locations]
if len(paragraph) in [pe[0] + pe[1] for pe in possible_endings]:
max_end_start = max([pe[0] for pe in possible_endings])
possible_endings = [pe for pe in possible_endings if pe[0] != max_end_start]
possible_endings = [pe[0] + pe[1] for pe in possible_endings if sum(pe) > len(paragraph) or (sum(pe) < len(paragraph) and paragraph[sum(pe)] == ' ')]
end = (-1 if not len(possible_endings) else max(possible_endings))
return end
def find_all(a_str, sub):
start = 0
while True:
start = a_str.find(sub, start)
if start == -1:
return
yield start
start += len(sub)
Я использовал функцию Karl find_all из этой записи: Найти все вхождения подстроки в Python
Я не очень хорош в регулярных выражениях, но более простая версия, "грубая сила" на самом деле, выше
sentence = re.compile("([\'\"][A-Z]|([A-Z][a-z]*\. )|[A-Z])(([a-z]*\.[a-z]*\.)|([A-Za-z0-9]*\.[A-Za-z0-9])|([A-Z][a-z]*\. [A-Za-z]*)|[^\.?]|[A-Za-z])*[\.?]")
что означает
начало приемлемых единиц: "[A-Z] или" [A-Z]
обратите внимание, большинство регулярных выражений жадные, поэтому порядок очень важен, когда мы делаем | (или). Вот почему я написал сначала i.e. регулярное выражение, а затем формы, такие как Inc.
Мой пример основан на примере Али, адаптированном к бразильскому португальцу. Спасибо Али.
ABREVIACOES = ['sra?s?', 'exm[ao]s?', 'ns?', 'nos?', 'doc', 'ac', 'publ', 'ex', 'lv', 'vlr?', 'vls?',
'exmo(a)', 'ilmo(a)', 'av', 'of', 'min', 'livr?', 'co?ls?', 'univ', 'resp', 'cli', 'lb',
'dra?s?', '[a-z]+r\(as?\)', 'ed', 'pa?g', 'cod', 'prof', 'op', 'plan', 'edf?', 'func', 'ch',
'arts?', 'artigs?', 'artg', 'pars?', 'rel', 'tel', 'res', '[a-z]', 'vls?', 'gab', 'bel',
'ilm[oa]', 'parc', 'proc', 'adv', 'vols?', 'cels?', 'pp', 'ex[ao]', 'eg', 'pl', 'ref',
'[0-9]+', 'reg', 'f[ilí]s?', 'inc', 'par', 'alin', 'fts', 'publ?', 'ex', 'v. em', 'v.rev']
ABREVIACOES_RGX = re.compile(r'(?:{})\.\s*$'.format('|\s'.join(ABREVIACOES)), re.IGNORECASE)
def sentencas(texto, min_len=5):
# baseado em https://stackoverflow.com/questions/25735644/python-regex-for-splitting-text-into-sentences-sentence-tokenizing
texto = re.sub(r'\s\s+', ' ', texto)
EndPunctuation = re.compile(r'([\.\?\!]\s+)')
# print(NonEndings)
parts = EndPunctuation.split(texto)
sentencas = []
sentence = []
for part in parts:
txt_sent = ''.join(sentence)
q_len = len(txt_sent)
if len(part) and len(sentence) and q_len >= min_len and \
EndPunctuation.match(sentence[-1]) and \
not ABREVIACOES_RGX.search(txt_sent):
sentencas.append(txt_sent)
sentence = []
if len(part):
sentence.append(part)
if sentence:
sentencas.append(''.join(sentence))
return sentencas
Полный код: https://github.com/luizanisio/comparador_elastic
Если вы хотите разбить предложения на 3 периода (не уверен, что это то, что вы хотите), вы можете использовать это регулярное выражение:
import re text = """\ Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't. """ sentences = re.split(r'\.{3}', text) for stuff in sentences: print(stuff)