Ich möchte eine Liste von Sätzen aus einer Zeichenfolge erstellen und sie dann ausdrucken. Ich möchte nicht NLTK verwenden, um dies zu tun. Es muss also nach einem Punkt am Ende des Satzes aufgeteilt werden und nicht nach Dezimalzahlen oder Abkürzungen oder Titel eines Namens oder wenn der Satz eine .com hat. Dies ist der Versuch einer Regex, der nicht funktioniert.
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)
Beispielausgabe wie es aussehen soll
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
Versuche dies. Teilen Sie Ihre Zeichenfolge hier auf. Sie können auch die Demo überprüfen.
Okay, Satz-Tokenizer sind etwas, das ich mir mit Regexes, nltk und CoreNLP genauer angesehen habe. Am Ende schreiben Sie Ihre eigenen und es hängt von der Anwendung ab. Dieses Zeug ist knifflig und wertvoll und die Leute geben nicht nur ihren Tokenizer-Code weiter. (Letztendlich ist die Tokenisierung kein deterministisches Verfahren, sondern wahrscheinlich und hängt auch sehr stark von Ihrem Korpus oder Ihrer Domain ab, z. B. Social-Media-Posts im Vergleich zu Yelp-Bewertungen im Vergleich zu ...)
Im Allgemeinen kann man sich nicht auf einen einzigen unfehlbaren Regex von Great White verlassen , man muss eine Funktion schreiben, die mehrere (sowohl positive als auch negative) Regexe verwendet; auch ein Wörterbuch der Abkürzungen und einige grundlegende Sprachanalyse, die weiß, dass z.B. 'I', 'USA', 'FCC', 'TARP' werden in englischer Sprache geschrieben.
Um zu veranschaulichen, wie leicht dies ernsthaft kompliziert werden kann, versuchen wir, Ihnen diese Funktionsspezifikation für einen deterministischen Tokenizer zu schreiben nur, um zu entscheiden, ob einzelne oder mehrere Punkte ('.'/'...') gibt das Ende des Satzes oder etwas anderes an:
function isEndOfSentence(leftContext, rightContext)
Im einfachen (deterministischen) Fall würde function isEndOfSentence(leftContext, rightContext)
einen Booleschen Wert zurückgeben, im allgemeineren Sinne ist dies jedoch probabilistisch: Es wird ein Gleitkommawert von 0.0-1.0 zurückgegeben (Konfidenzniveau, bei dem dieses bestimmte '.' Ein Satzende ist). .
Referenzen: [a] Coursera-Video: "Grundlegende Textverarbeitung 2-5 - Satzsegmentierung - Stanford NLP - Professor Dan Jurafsky & Chris Manning" [UPDATE: eine inoffizielle Version, die früher auf YouTube war, wurde entfernt]
Versuchen Sie, die Eingabe nach den Leerzeichen und nicht nach einem Punkt oder ?
aufzuteilen. Wenn Sie dies tun, wird der Punkt oder ?
nicht im Endergebnis gedruckt.
>>> 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
Hier ist der verwendete Regex: (?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)(\s|[A-Z].*)
Erster Block: (?<!\w\.\w.)
: Dieses Muster sucht in einer negativen Rückkopplungsschleife (?<!)
nach allen Wörtern (\w)
, gefolgt von fullstop (\.)
, gefolgt von anderen Wörtern (\.)
.
Zweiter Block: (?<![A-Z][a-z]\.)
: Dieses Muster sucht in einer negativen Rückkopplungsschleife nach etwas, das mit Großbuchstaben ([A-Z])
beginnt, gefolgt von Kleinbuchstaben ([a-z])
, bis ein Punkt (\.)
gefunden wird.
Dritter Block: (?<=\.|\?)
: Dieses Muster sucht in einer Rückkopplungsschleife von Punkt (\.)
OR Fragezeichen (\?)
Vierter Block: (\s|[A-Z].*)
: Dieses Muster sucht nach dem Fragezeichen OR aus dem dritten Block. Es sucht nach Leerzeichen (\s)
OR, einer beliebigen Zeichenfolge, die mit einem Großbuchstaben ([A-Z].*)
..__ beginnt. Dieser Block ist wichtig zu teilen, wenn die Eingabe as ist
Hallo world.Hi ich bin heute hier.
d.h. wenn nach dem Punkt Platz oder kein Platz vorhanden ist.
Versuche dies:
(?<!\b(?:[A-Z][a-z]|\d|[i.e]))\.(?!\b(?:com|\d+)\b)
Mein Beispiel basiert auf dem Beispiel von ALi, angepasst an brasilianisches Portugiesisch. Danke ALi.
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
Vollständiger Code in: https://github.com/luizanisio/comparador_elastic
Naiver Ansatz für korrekte englische Sätze, die nicht mit Nicht-Alphas beginnen und keine zitierten Wortteile enthalten:
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))
Falsch positive Aufteilung kann reduziert werden, indem NonEndings etwas erweitert wird. Für andere Fälle wird zusätzlicher Code benötigt. Ein vernünftiger Umgang mit Tippfehlern wird bei diesem Ansatz schwierig sein.
Mit diesem Ansatz werden Sie niemals Perfektion erreichen. Aber je nach Aufgabe klappt es vielleicht "genug" ...
Ich habe dies unter Berücksichtigung der oben genannten Kommentare von smci geschrieben. Es ist ein Mittelweg, der keine externen Bibliotheken erfordert und keinen Regex verwendet. Damit können Sie eine Liste von Abkürzungen und Konten für Sätze bereitstellen, die durch Abschlusszeichen in Wrappern beendet werden, z. B. ein Punkt und ein Zitat: [. ",? ',.)].
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)
Ich habe Karls find_all-Funktion aus diesem Eintrag verwendet: Alle Vorkommen eines Teilstrings in Python suchen
Ich bin nicht gut in regulären Ausdrücken, aber eine einfachere Version, "rohe Gewalt", ist eigentlich von oben
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])*[\.?]")
was bedeutet akzeptable Einheiten sind '[A-Z] oder "[A-Z]
Bitte beachten Sie, dass die meisten regulären Ausdrücke gierig sind, daher ist die Reihenfolge sehr wichtig, wenn wir |(oder). Deshalb habe ich zuerst geschrieben. Regulärer Ausdruck, dann kommen Formen wie Inc..