Подтвердить что ты не робот

Python разделяет текст на предложения

У меня есть текстовый файл. Мне нужен список предложений.

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

Мой старый regexp работает плохо.

re.compile('(\. |^|!|\?)([A-Z][^;↑\.<>@\^&/\[\]]*(\.|!|\?) )',re.M)
4b9b3361

Ответ 1

Инструмент Natural Language Toolkit (nltk.org) имеет то, что вам нужно. Эта публикация в группе указывает, что это делает:

import nltk.data

tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
fp = open("test.txt")
data = fp.read()
print '\n-----\n'.join(tokenizer.tokenize(data))

(Я не пробовал!)

Ответ 2

Эта функция может разделить весь текст Гекльберри Финна на предложения примерно за 0,1 секунды и обрабатывать многие из более болезненных краевых случаев, которые делают предложение пессимизма нетривиальным, например: "Г-н Джон Джонсон-младший родился в США, но получил его Ph. D. в Израиле, прежде чем присоединиться к Nike Inc. в качестве инженера. Он также работал на Craigslist.org в качестве бизнес-аналитика ".

# -*- coding: utf-8 -*-
import re
alphabets= "([A-Za-z])"
prefixes = "(Mr|St|Mrs|Ms|Dr)[.]"
suffixes = "(Inc|Ltd|Jr|Sr|Co)"
starters = "(Mr|Mrs|Ms|Dr|He\s|She\s|It\s|They\s|Their\s|Our\s|We\s|But\s|However\s|That\s|This\s|Wherever)"
acronyms = "([A-Z][.][A-Z][.](?:[A-Z][.])?)"
websites = "[.](com|net|org|io|gov)"

def split_into_sentences(text):
    text = " " + text + "  "
    text = text.replace("\n"," ")
    text = re.sub(prefixes,"\\1<prd>",text)
    text = re.sub(websites,"<prd>\\1",text)
    if "Ph.D" in text: text = text.replace("Ph.D.","Ph<prd>D<prd>")
    text = re.sub("\s" + alphabets + "[.] "," \\1<prd> ",text)
    text = re.sub(acronyms+" "+starters,"\\1<stop> \\2",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>\\3<prd>",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>",text)
    text = re.sub(" "+suffixes+"[.] "+starters," \\1<stop> \\2",text)
    text = re.sub(" "+suffixes+"[.]"," \\1<prd>",text)
    text = re.sub(" " + alphabets + "[.]"," \\1<prd>",text)
    if """ in text: text = text.replace("."","".")
    if "\"" in text: text = text.replace(".\"","\".")
    if "!" in text: text = text.replace("!\"","\"!")
    if "?" in text: text = text.replace("?\"","\"?")
    text = text.replace(".",".<stop>")
    text = text.replace("?","?<stop>")
    text = text.replace("!","!<stop>")
    text = text.replace("<prd>",".")
    sentences = text.split("<stop>")
    sentences = sentences[:-1]
    sentences = [s.strip() for s in sentences]
    return sentences

Ответ 3

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

>>> from nltk import tokenize
>>> p = "Good morning Dr. Adams. The patient is waiting for you in room number 3."

>>> tokenize.sent_tokenize(p)
['Good morning Dr. Adams.', 'The patient is waiting for you in room number 3.']

ref: fooobar.com/questions/100339/...

Ответ 4

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

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

Ответ 5

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

import spacy
nlp = spacy.load('en')

text = '''Your text here'''
tokens = nlp(text)

for sent in tokens.sents:
    print(sent.string.strip())

Ответ 6

Для простых случаев (когда предложения заканчиваются нормально), это должно работать:

import re
text = ''.join(open('somefile.txt').readlines())
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)

Регулярное выражение *\. +, которое соответствует периоду, окруженному 0 или более пробелами влево, и 1 или более справа (чтобы исключить что-то вроде периода в re.split, считающегося изменением предложения).

Очевидно, что это не самое надежное решение, но в большинстве случаев это будет хорошо. Единственный случай, на который это не распространяется, - это сокращения (возможно, через список предложений и проверить, что каждая строка в sentences начинается с заглавной буквы?)

Ответ 7

@Artyom,

Привет! Вы можете создать новый токенизатор для русского (и некоторых других языков) с помощью этой функции:

def russianTokenizer(text):
    result = text
    result = result.replace('.', ' . ')
    result = result.replace(' .  .  . ', ' ... ')
    result = result.replace(',', ' , ')
    result = result.replace(':', ' : ')
    result = result.replace(';', ' ; ')
    result = result.replace('!', ' ! ')
    result = result.replace('?', ' ? ')
    result = result.replace('\"', ' \" ')
    result = result.replace('\'', ' \' ')
    result = result.replace('(', ' ( ')
    result = result.replace(')', ' ) ') 
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.strip()
    result = result.split(' ')
    return result

а затем вызовите его следующим образом:

text = 'вы выполняете поиск, используя Google SSL;'
tokens = russianTokenizer(text)

Удачи, Marilena.

Ответ 8

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

Итак, здесь приведен простой код на основе http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-into-sentences.html

# split up a paragraph into sentences
# using regular expressions


def splitParagraphIntoSentences(paragraph):
    ''' break a paragraph into sentences
        and return a list '''
    import re
    # to split by multile characters

    #   regular expressions are easiest (and fastest)
    sentenceEnders = re.compile('[.!?]')
    sentenceList = sentenceEnders.split(paragraph)
    return sentenceList


if __name__ == '__main__':
    p = """This is a sentence.  This is an excited sentence! And do you think this is a question?"""

    sentences = splitParagraphIntoSentences(p)
    for s in sentences:
        print s.strip()

#output:
#   This is a sentence
#   This is an excited sentence

#   And do you think this is a question 

Ответ 9

Мне пришлось читать файлы субтитров и разделить их на предложения. После предварительной обработки (например, удаления информации о времени и т.д. В файлах.srt) переменная fullFile содержала полный текст файла субтитров. Следующий грубый путь аккуратно разделил их на предложения. Вероятно, мне повезло, что предложения всегда заканчивались (правильно) пробелом. Попробуйте это первым, и если у него есть какие-то исключения, добавьте больше сдержек и противовесов.

# Very approximate way to split the text into sentences - Break after ? . and !
fullFile = re.sub("(\!|\?|\.) ","\\1<BRK>",fullFile)
sentences = fullFile.split("<BRK>");
sentFile = open("./sentences.out", "w+");
for line in sentences:
    sentFile.write (line);
    sentFile.write ("\n");
sentFile.close;

Ой! Что ж. Теперь я понимаю, что, поскольку мой контент был испанский, у меня не было проблем с "мистером Смитом" и т.д. Тем не менее, если кто-то хочет быстрый и грязный парсер...

Ответ 10

Вы также можете использовать функцию токенизации предложений в NLTK:

from nltk.tokenize import sent_tokenize
sentence = "As the most quoted English writer Shakespeare has more than his share of famous quotes.  Some Shakespare famous quotes are known for their beauty, some for their everyday truths and some for their wisdom. We often talk about Shakespeares quotes as things the wise Bard is saying to us but, we should remember that some of his wisest words are spoken by his biggest fools. For example, both ‘neither a borrower nor a lender be, and ‘to thine own self be true are from the foolish, garrulous and quite disreputable Polonius in Hamlet."

sent_tokenize(sentence)