web-dev-qa-db-ja.com

文字列を単語と句読点に分割する

文字列を単語と句読点に分割し、分割されたリストに句読点を追加しようとしています。

例えば:

>>> c = "help, me"
>>> print c.split()
['help,', 'me']

私がリストを本当に見せたいのは:

['help', ',', 'me']

したがって、文字列を空白で分割し、句読点を単語から分割する必要があります。

私は最初に文字列を解析してから分割を実行しようとしました:

>>> for character in c:
...     if character in ".,;!?":
...             outputCharacter = " %s" % character
...     else:
...             outputCharacter = character
...     separatedPunctuation += outputCharacter
>>> print separatedPunctuation
help , me
>>> print separatedPunctuation.split()
['help', ',', 'me']

これにより、希望どおりの結果が得られますが、大きなファイルでは非常に遅くなります。

これをより効率的に行う方法はありますか?

58
David A

これは多かれ少なかれそれを行う方法です:

>>> import re
>>> re.findall(r"[\w']+|[.,!?;]", "Hello, I'm a string!")
['Hello', ',', "I'm", 'a', 'string', '!']

秘Theは、文字列をどこで分割するかではなく、トークンに何を含めるかを考えることです。

警告:

  • アンダースコア(_)は、単語内文字と見なされます。必要ない場合は、\ wを置き換えます。
  • これは、ストリング内の(単一の)引用符では機能しません。
  • 使用する追加の句読点を正規表現の右半分に入れます。
  • Reで明示的に言及されていないものは黙って削除されます。
79
user3850

Unicode対応バージョンは次のとおりです。

re.findall(r"\w+|[^\w\s]", text, re.UNICODE)

最初の選択肢は、Word文字のシーケンスをキャッチします(ユニコードで定義されているため、「履歴書」は['r', 'sum']になりません)。 2番目は、空白を無視して、個々の非Word文字をキャッチします。

トップアンサーとは異なり、これは一重引用符を個別の句読点として処理することに注意してください(例: "I'm"-> ['I', "'", 'm'])。これはNLPの標準のようであるため、機能と考えています。

32
LaC

Perlスタイルの正規表現構文では、\bは、Wordの境界に一致します。これは、正規表現ベースの分割を行うのに便利です。

編集:「空の一致」がPythonのreモジュールのsplit関数では機能しないことをホップから通知されました。この「機能」に困惑している人のための情報として、これをここに残します。

5
Svante

これが私のエントリーです。

効率という意味でこれがどれだけうまくいくのか、またはすべてのケースをキャッチするのかについて疑問があります(グループ化された「!!!」に注意してください。これは良いことかもしれませんし、そうでないかもしれません)。

>>> import re
>>> import string
>>> s = "Helo, my name is Joe! and i live!!! in a button; factory:"
>>> l = [item for item in map(string.strip, re.split("(\W+)", s)) if len(item) > 0]
>>> l
['Helo', ',', 'my', 'name', 'is', 'Joe', '!', 'and', 'i', 'live', '!!!', 'in', 'a', 'button', ';', 'factory', ':']
>>>

明白な最適化の1つは、行単位でこれを行う場合は、事前に正規表現をコンパイルすることです(re.compileを使用)。

3
Chris Cameron

実装のマイナーアップデートを以下に示します。あなたがもっと詳細なことをしようとするなら、ル・ドルフィエが示唆したNLTKを調べることをお勧めします。

+ =の代わりに '' .join()が使用されるため、これは少し速くなるかもしれません。これは 高速であることが知られています です。

import string

d = "Hello, I'm a string!"

result = []
Word = ''

for char in d:
    if char not in string.whitespace:
        if char not in string.ascii_letters + "'":
            if Word:
                    result.append(Word)
            result.append(char)
            Word = ''
        else:
            Word = ''.join([Word,char])

    else:
        if Word:
            result.append(Word)
            Word = ''
print result
['Hello', ',', "I'm", 'a', 'string', '!']
1
monkut

特にpythonを使用しているため、 [〜#〜] nltk [〜#〜] で想像できるすべてのヘルプを見つけることができると思います。チュートリアルには、この問題に関する総合的な説明があります。

0
dkretz

これを試して:

string_big = "One of Python's coolest features is the string format operator  This operator is unique to strings"
my_list =[]
x = len(string_big)
poistion_ofspace = 0
while poistion_ofspace < x:
    for i in range(poistion_ofspace,x):
        if string_big[i] == ' ':
            break
        else:
            continue
    print string_big[poistion_ofspace:(i+1)]
    my_list.append(string_big[poistion_ofspace:(i+1)])
    poistion_ofspace = i+1

print my_list
0

英語(または他の一般的な言語)で作業する場合は、 [〜#〜] nltk [〜#〜]FreeLing )など、これを行う他の多くのツールがあります。

import nltk
sentence = "help, me"
nltk.Word_tokenize(sentence)

ハードコーディングを必要としない\W+を使用して、すべての単語と\bパターンをトークン化する方法を思い付きました。

>>> import re
>>> sentence = 'Hello, world!'
>>> tokens = [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', sentence)]
['Hello', ',', 'world', '!']

ここで、.*?\S.*?はスペース以外のすべてに一致するパターンであり、$は、句読記号の場合に文字列の最後のトークンに一致するように追加されます。

ただし、次の点に注意してください-これにより、複数の記号で構成される句読点がグループ化されます。

>>> print [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"Oh no", she said')]
['Oh', 'no', '",', 'she', 'said']

もちろん、次のようにしてそのようなグループを見つけて分割できます。

>>> for token in [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"You can", she said')]:
...     print re.findall(r'(?:\w+|\W)', token)

['You']
['can']
['"', ',']
['she']
['said']
0
FrauHahnhen