Python divide texto en oraciones

Tengo un archivo de texto. Necesito obtener una lista de oraciones.

¿Cómo se puede implementar esto? Hay muchas sutilezas, como el punto que se usa en las abreviaturas.

Mi antigua expresión regular funciona mal.

re.compile('(\. |^|!|\?)([AZ][^;↑\.@\^&/\[\]]*(\.|!|\?) )',re.M) 

Related of "Python divide texto en oraciones"

El kit de herramientas de lenguaje natural ( nltk.org ) tiene lo que necesita. Esta publicación grupal indica que esto lo hace:

 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)) 

(¡No lo he probado!)

Esta función puede dividir el texto completo de Huckleberry Finn en oraciones en aproximadamente 0.1 segundos y maneja muchos de los casos más dolorosos que hacen que el análisis de oraciones no sea trivial, por ejemplo, “el Sr. John Johnson Jr. nació en los Estados Unidos pero obtuvo su Ph. D. en Israel antes de unirse a Nike Inc. como ingeniero. También trabajó en craigslist.org como analista de negocios.

 # -*- 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 = "([AZ][.][AZ][.](?:[AZ][.])?)" websites = "[.](com|net|org|io|gov)" def split_into_sentences(text): text = " " + text + " " text = text.replace("\n"," ") text = re.sub(prefixes,"\\1",text) text = re.sub(websites,"\\1",text) if "Ph.D" in text: text = text.replace("Ph.D.","PhD") text = re.sub("\s" + alphabets + "[.] "," \\1 ",text) text = re.sub(acronyms+" "+starters,"\\1 \\2",text) text = re.sub(alphabets + "[.]" + alphabets + "[.]" + alphabets + "[.]","\\1\\2\\3",text) text = re.sub(alphabets + "[.]" + alphabets + "[.]","\\1\\2",text) text = re.sub(" "+suffixes+"[.] "+starters," \\1 \\2",text) text = re.sub(" "+suffixes+"[.]"," \\1",text) text = re.sub(" " + alphabets + "[.]"," \\1",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(".",".") text = text.replace("?","?") text = text.replace("!","!") text = text.replace("",".") sentences = text.split("") sentences = sentences[:-1] sentences = [s.strip() for s in sentences] return sentences 

En lugar de usar expresiones regulares para dividir el texto en oraciones, también puede usar la biblioteca 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: https://stackoverflow.com/a/9474645/2877052

Este es un enfoque intermedio que no se basa en ninguna biblioteca externa. Utilizo la comprensión de listas para excluir superposiciones entre abreviaturas y terminadores, así como para excluir superposiciones entre variaciones en terminaciones, por ejemplo: ‘.’ vs. ‘. “‘

 abbreviations = {'dr.': 'doctor', 'mr.': 'mister', 'bro.': 'brother', 'bro': 'brother', 'mrs.': 'mistress', 'ms.': 'miss', 'jr.': 'junior', 'sr.': 'senior', 'ie': 'for example', 'eg': '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) 

Usé la función find_all de Karl de esta entrada: Encuentre todas las ocurrencias de una subcadena en Python

Puedes intentar usar Spacy en lugar de expresiones regulares. Lo uso y hace el trabajo.

 import spacy nlp = spacy.load('en') text = '''Your text here''' tokens = nlp(text) for sent in tokens.sents: print(sent.string.strip()) 

Para casos simples (donde las oraciones terminan normalmente), esto debería funcionar:

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

La expresión regular es *\. + *\. + , que coincide con un período rodeado por 0 o más espacios a la izquierda y 1 o más a la derecha (para evitar que algo como el período en re.split se cuente como un cambio en la oración).

Obviamente, no es la solución más robusta, pero funcionará bien en la mayoría de los casos. El único caso que no cubre son las abreviaturas (¿quizás repasar la lista de oraciones y verificar que cada cadena en las sentences comience con una letra mayúscula?

@Artyom,

¡Hola! Podría crear un nuevo tokenizador para el ruso (y algunos otros idiomas) utilizando esta función:

 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 

y luego llámalo de esta manera:

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

Buena suerte, Marilena.

No hay duda de que NLTK es el más adecuado para el propósito. Pero comenzar con NLTK es bastante doloroso (pero una vez que lo instalas, simplemente obtienes las recompensas)

Así que aquí está el código basado en el código simple disponible en 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 

Tuve que leer los archivos de subtítulos y dividirlos en oraciones. Después del preprocesamiento (como eliminar la información de tiempo, etc. en los archivos .srt), la variable fullFile contenía el texto completo del archivo de subtítulos. La siguiente manera cruda los divide en oraciones. Probablemente tuve la suerte de que las oraciones siempre terminaban (correctamente) con un espacio. Pruebe esto primero y si tiene alguna excepción, agregue más cheques y saldos.

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

Oh! bien. Ahora me doy cuenta de que, dado que mi contenido era español, no tenía los problemas de tratar con el “Sr. Smith”, etc. Sin embargo, si alguien quiere un analizador rápido y sucio …