文字列から文章のリストを作成し、それらを印刷したい。これを行うために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
これを試して。これを文字列に分割します。デモを確認することもできます。
わかりましたので、文トケナイザーは、正規表現、nltk、CoreNLPを使用して、私が少し詳しく調べたものです。あなたは自分で書くことになり、それはアプリケーションに依存します。このようなものはトリッキーで価値があり、人々は自分のトークナイザーコードをただ渡すだけではありません。 (最終的に、トークン化は決定論的な手順ではなく、確率的であり、コーパスまたはドメインにも非常に大きく依存します(例:ソーシャルメディアの投稿対Yelpのレビュー対...)
一般に、1つのグレートホワイトの絶対的な正規表現に依存することはできません。いくつかの正規表現(正と負の両方)を使用する関数を記述する必要があります。また、略語の辞書、およびそれを知っているいくつかの基本的な言語解析。 「I」、「USA」、「FCC」、「TARP」は大文字で表記されます。
これがどれほど簡単に非常に複雑になるかを説明するために、決定論的トークナイザーの機能仕様justを書いて、単一か複数かを決定してみましょうピリオド( '。'/'...')は文末、または他の何かを示します:
function isEndOfSentence(leftContext, rightContext)
単純な(決定論的な)場合、function isEndOfSentence(leftContext, rightContext)
はブール値を返しますが、より一般的な意味では確率的です:0.0-1.0のfloatを返します(特定の '。'が文末であるという信頼レベル) 。
参照:[a] Courseraのビデオ:「基本的なテキスト処理2-5-文のセグメンテーション-スタンフォードNLP-ダンジュラフスキー&クリスマニング教授」 [更新: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)
に続いてフルストップ(\.)
、他の単語が続く(\.)
2番目のブロック:(?<![A-Z][a-z]\.)
:このパターンは、大文字のアルファベットで始まるものをネガティブフィードバックループで検索します([A-Z])
、小文字のアルファベット([a-z])
ドットまで(\.)
見つかった。
3番目のブロック:(?<=\.|\?)
:このパターンは、ドット(\.)
OR疑問符(\?)
4番目のブロック:(\s|[A-Z].*)
:このパターンは、3番目のブロックのドットOR疑問符の後に検索します。空白を検索します(\s)
OR大文字のアルファベットで始まる文字列([A-Z].*)
。このブロックは、入力が次の場合に分割することが重要です
Hello world.Hi今日はここにいます。
すなわち、ドットの後にスペースがあるか、スペースがない場合。
これを試して:
(?<!\b(?:[A-Z][a-z]|\d|[i.e]))\.(?!\b(?:com|\d+)\b)
私の例は、ブラジルポルトガル語に適応した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
適切な英語の文が非アルファで始まっておらず、引用された品詞が含まれていないための素朴なアプローチ:
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]
注意してください、ほとんどの正規表現は貪欲であるため、(or)を実行するときの順序は非常に重要です。それが、なぜ私がi.e。正規表現を最初に書いたのか、そしてInc。