Pythonで文字列から英数字以外のすべてを取り除く


336

Pythonを使用して、文字列から英数字以外のすべての文字を削除する最良の方法は何ですか?

この質問のPHPバリアントで提示される解決策は、おそらくいくつかの小さな調整で機能しますが、私にはあまり「Pythonic」ではないようです。

参考までに、ピリオドとコンマ(およびその他の句読点)だけでなく、引用符、角かっこなども削除する必要があります。


7
「æøå」、「مرحبا」、「สวัสดี」、「こんにちは」などの国際的な英数字を気にしますか?
Pimin Konstantin Kefaloukos 2014年

4
@PiminKonstantinKefaloukosはい私は国際的な文字を気にしているので、re.UNICODEを使用するための受け入れられた回答に対する私のコメント。
Mark van Lent

回答:


335

好奇心からいくつかの機能の時間を計ったところです。これらのテストでは、文字列string.printable(組み込みstringモジュールの一部)から英数字以外の文字を削除しています。コンパイルの使用'[\W_]+'とは、pattern.sub('', str)最速であることが判明しました。

$ python -m timeit -s \
     "import string" \
     "''.join(ch for ch in string.printable if ch.isalnum())" 
10000 loops, best of 3: 57.6 usec per loop

$ python -m timeit -s \
    "import string" \
    "filter(str.isalnum, string.printable)"                 
10000 loops, best of 3: 37.9 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]', '', string.printable)"
10000 loops, best of 3: 27.5 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]+', '', string.printable)"                
100000 loops, best of 3: 15 usec per loop

$ python -m timeit -s \
    "import re, string; pattern = re.compile('[\W_]+')" \
    "pattern.sub('', string.printable)" 
100000 loops, best of 3: 11.2 usec per loop

2
非常に興味深い結果:正規表現が遅くなることを期待していました。興味深いことに、私は1つの、他のオプション(でこれを試してみましたvalid_characters = string.ascii_letters + string.digitsが続くjoin(ch for ch in string.printable if ch in valid_characters)と、それはより速くより6マイクロ秒だったisalnum()。。オプションそれでもかなり遅い正規表現よりもかかわらず
DRAL

+1、測定時間は良いです!(しかし、最後から2番目ではpattern.sub('', string.printable)、REオブジェクトがあるときにre.subを呼び出すのはばかげています!-)。
Alex Martelli、

46
記録のために:re.compile('[\W_]+', re.UNICODE)それをunicode安全にするために使用します。
マークヴァンレント

3
空白を削除せずにそれをどのように行うのですか?
maudulus 14

6
空白を削除せずにそれを行います:re.sub( '[\ W _] +'、 ''、statement、flags = re.UNICODE)
PALEN

267

救助のための正規表現:

import re
re.sub(r'\W+', '', your_string)

Pythonの定義により'\W== [^a-zA-Z0-9_]、除外すべてnumberslettersおよび_


2
正符号は正規表現で何をしますか?(私はそれが何を意味するのか知っています、なぜそれがreに必要なのかについて興味があります)
Mark van Lent

7
@マーク:置換によってブロック内のすべての非単語文字が1つずつ削除されるのではなく、一度に削除されるため、置換が高速化すると思います。
DrAl 2009

2
ええ、少し前にパフォーマンスクリティカルなコードを調整しながらベンチマークを行いました。かなりのスパンの文字がある場合、スピードアップは非常に大きくなります。
Ants Aasma 2009

20
この場合は関係ないかもしれませんが、\Wアンダースコアも保持します。
Blixt 2009

12
あなたは文字と数字のみを使用して、re.sub(R '[^-ZA-Z0-9]'、 ''、your_string)を行うことができますしたい場合は、@Blixt先端に続いて
Nigini

68

str.translate()メソッドを使用します。

これを頻繁に行うと想定します。

(1)一度、削除したいすべての文字を含む文字列を作成します。

delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())

(2)文字列を削除したいときはいつでも:

scrunched = s.translate(None, delchars)

セットアップのコストは、おそらくre.compileに匹敵します。限界費用はずっと低いです:

C:\junk>\python26\python -mtimeit -s"import string;d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s=string.printable" "s.translate(None,d)"
100000 loops, best of 3: 2.04 usec per loop

C:\junk>\python26\python -mtimeit -s"import re,string;s=string.printable;r=re.compile(r'[\W_]+')" "r.sub('',s)"
100000 loops, best of 3: 7.34 usec per loop

注:string.printableをベンチマークデータとして使用すると、パターン '[\ W _] +'が不当に有利になります。英数字以外のすべての文字が1つの束に含まれています...一般的なデータでは、複数の置換が必要です。

C:\junk>\python26\python -c "import string; s = string.printable; print len(s),repr(s)"
100 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

re.subにもう少し作業を行わせると、次のようになります。

C:\junk>\python26\python -mtimeit -s"d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s='foo-'*25" "s.translate(None,d)"
1000000 loops, best of 3: 1.97 usec per loop

C:\junk>\python26\python -mtimeit -s"import re;s='foo-'*25;r=re.compile(r'[\W_]+')" "r.sub('',s)"
10000 loops, best of 3: 26.4 usec per loop

1
変換を使用すると、確かにかなり高速になります。置換/変換を実行する直前にforループを追加する場合でも(セットアップコストを少なくするため)、変換は私のマシンの正規表現よりも約17倍速くなります。知ってよかった。
マークヴァンレント

3
これは間違いなく最もpythonicソリューションです。
codygman 2012

1
これは、ほとんど私を納得させるが、私は使用することをお勧めしますstring.punctuation代わりに''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols

1
これはstrオブジェクトに対しては機能しますが、オブジェクトに対しては機能しないことに注意してくださいunicode
Yavar 2015年

@John Machinこれは本質的に、引数として渡されるリスト内包.join()ですか?
AdjunctProfessorFalcon 2016

41

あなたは試すことができます:

print ''.join(ch for ch in some_string if ch.isalnum())

15
>>> import re
>>> string = "Kl13@£$%[};'\""
>>> pattern = re.compile('\W')
>>> string = re.sub(pattern, '', string)
>>> print string
Kl13

私はあなたの答えが好きでしたが、アラビア語の文字も削除されます。それらを保持する方法を教えてください
Charif DZ

13

どうですか:

def ExtractAlphanumeric(InputString):
    from string import ascii_letters, digits
    return "".join([ch for ch in InputString if ch in (ascii_letters + digits)])

これは、リスト内包表記を使用して、文字がInputString結合さascii_lettersれたdigits文字列に存在する場合に、文字のリストを生成することによって機能します。次に、リストを結合して文字列にします。


string.ascii_lettersには文字(duh)のみが含まれ、数字は含まれていないようです。数字も必要です...
マークファンレント

string.digitsを追加すると、先ほど述べた問題が実際に解決されます。:)
マークヴァンレント

はい、私が戻ってあなたの質問を読んだときに気づきました。自分への注意:読むことを学ぶ!
DrAl 2009

4

ここで他のいくつかの回答からのスピンオフとして、文字列のコンテンツを制限したい文字のセットを定義するための本当にシンプルで柔軟な方法を提供します。この場合、英数字とダッシュとアンダースコアを使用できます。PERMITTED_CHARSユースケースに合わせて、myに文字を追加または削除してください。

PERMITTED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" 
someString = "".join(c for c in someString if c in PERMITTED_CHARS)

2
微妙なエラーが発生しやすい許可された文字をハードコーディングする代わりに、を使用しますstring.digits + string.ascii_letters + '_-'
Reti43

あなたの提案は間違っていませんが、それがあなたの目標である場合、それはまた、「タイピング」の多くの文字を保存しません。私の投稿をコピーすると、タイプミスもなくなります!ただし、私の答えの本当のポイントは、どの文字を許可するかを正確に定義するための明示的で制限のない簡単な手段を許可することです。
BuvinJ 2017年

SPECIAL_CHARS = '_-'string.digits + string.ascii_letters + SPECIAL_CHARS
中立的な立場

コードゴルフをしているのでない限り、それは合理的であるという点での提案でした。52個のアルファベットを入力するためにキーボードの周りを「歩く」と、オブジェクトを使用するためにパッケージをインポートするよりもかなり時間がかかります。そして、それはあなたがそれをすべて正しくタイプしたことを再確認する時間を含みません。それは良い習慣についてです、それだけです。
Reti43

私はあなたを聞く!ここでの私の本当のポイントは、文字セットをより具体的にしたい場合に備えて、極端な柔軟性です。
BuvinJ 2017年

4
sent = "".join(e for e in sent if e.isalpha())

説明しようと思います:それはすべての文字列文字を調べ、現在の文字がアルファベット記号であるかどうかステートメントe for e in sentでチェックします。if e.isalpha()そうであれば、それをsent変数に結合し、sent = "".join()アルファベット以外の記号はすべて""(空の文字列)に置き換えられます。join機能。
Sysanin

これはCの正規表現に依存するのではなく、文字ごとにループを実行しているので、これは非常に遅いのではないですか?
dcsan


2

ASCII印刷可能ファイルのランダムな文字列によるタイミング:

from inspect import getsource
from random import sample
import re
from string import printable
from timeit import timeit

pattern_single = re.compile(r'[\W]')
pattern_repeat = re.compile(r'[\W]+')
translation_tb = str.maketrans('', '', ''.join(c for c in map(chr, range(256)) if not c.isalnum()))


def generate_test_string(length):
    return ''.join(sample(printable, length))


def main():
    for i in range(0, 60, 10):
        for test in [
            lambda: ''.join(c for c in generate_test_string(i) if c.isalnum()),
            lambda: ''.join(filter(str.isalnum, generate_test_string(i))),
            lambda: re.sub(r'[\W]', '', generate_test_string(i)),
            lambda: re.sub(r'[\W]+', '', generate_test_string(i)),
            lambda: pattern_single.sub('', generate_test_string(i)),
            lambda: pattern_repeat.sub('', generate_test_string(i)),
            lambda: generate_test_string(i).translate(translation_tb),

        ]:
            print(timeit(test), i, getsource(test).lstrip('            lambda: ').rstrip(',\n'), sep='\t')


if __name__ == '__main__':
    main()

結果(Python 3.7):

       Time       Length                           Code                           
6.3716264850008880  00  ''.join(c for c in generate_test_string(i) if c.isalnum())
5.7285426190064750  00  ''.join(filter(str.isalnum, generate_test_string(i)))
8.1875841680011940  00  re.sub(r'[\W]', '', generate_test_string(i))
8.0002205439959650  00  re.sub(r'[\W]+', '', generate_test_string(i))
5.5290945199958510  00  pattern_single.sub('', generate_test_string(i))
5.4417179649972240  00  pattern_repeat.sub('', generate_test_string(i))
4.6772285089973590  00  generate_test_string(i).translate(translation_tb)
23.574712151996210  10  ''.join(c for c in generate_test_string(i) if c.isalnum())
22.829975890002970  10  ''.join(filter(str.isalnum, generate_test_string(i)))
27.210196289997840  10  re.sub(r'[\W]', '', generate_test_string(i))
27.203713296003116  10  re.sub(r'[\W]+', '', generate_test_string(i))
24.008979928999906  10  pattern_single.sub('', generate_test_string(i))
23.945240008994006  10  pattern_repeat.sub('', generate_test_string(i))
21.830899796994345  10  generate_test_string(i).translate(translation_tb)
38.731336012999236  20  ''.join(c for c in generate_test_string(i) if c.isalnum())
37.942474347000825  20  ''.join(filter(str.isalnum, generate_test_string(i)))
42.169366310001350  20  re.sub(r'[\W]', '', generate_test_string(i))
41.933375883003464  20  re.sub(r'[\W]+', '', generate_test_string(i))
38.899814646996674  20  pattern_single.sub('', generate_test_string(i))
38.636144253003295  20  pattern_repeat.sub('', generate_test_string(i))
36.201238164998360  20  generate_test_string(i).translate(translation_tb)
49.377356811004574  30  ''.join(c for c in generate_test_string(i) if c.isalnum())
48.408927293996385  30  ''.join(filter(str.isalnum, generate_test_string(i)))
53.901889764994850  30  re.sub(r'[\W]', '', generate_test_string(i))
52.130339455994545  30  re.sub(r'[\W]+', '', generate_test_string(i))
50.061149017004940  30  pattern_single.sub('', generate_test_string(i))
49.366573111998150  30  pattern_repeat.sub('', generate_test_string(i))
46.649754120997386  30  generate_test_string(i).translate(translation_tb)
63.107938601999194  40  ''.join(c for c in generate_test_string(i) if c.isalnum())
65.116287978999030  40  ''.join(filter(str.isalnum, generate_test_string(i)))
71.477421126997800  40  re.sub(r'[\W]', '', generate_test_string(i))
66.027950693998720  40  re.sub(r'[\W]+', '', generate_test_string(i))
63.315361931003280  40  pattern_single.sub('', generate_test_string(i))
62.342320287003530  40  pattern_repeat.sub('', generate_test_string(i))
58.249303059004890  40  generate_test_string(i).translate(translation_tb)
73.810345625002810  50  ''.join(c for c in generate_test_string(i) if c.isalnum())
72.593953348005020  50  ''.join(filter(str.isalnum, generate_test_string(i)))
76.048324580995540  50  re.sub(r'[\W]', '', generate_test_string(i))
75.106637657001560  50  re.sub(r'[\W]+', '', generate_test_string(i))
74.681338128997600  50  pattern_single.sub('', generate_test_string(i))
72.430461594005460  50  pattern_repeat.sub('', generate_test_string(i))
69.394243567003290  50  generate_test_string(i).translate(translation_tb)

str.maketransstr.translateが最も高速ですが、すべての非ASCII文字が含まれます。 re.compilepattern.subは低速ですが、''.join&よりも高速ですfilter


-1

私が正しく理解している場合、最も簡単な方法は、多くの柔軟性を提供する正規表現を使用することです。

s = """An... essay is, generally, a piece of writing that gives the author's own 
argument — but the definition is vague, 
overlapping with those of a paper, an article, a pamphlet, and a short story. Essays 
have traditionally been 
sub-classified as formal and informal. Formal essays are characterized by "serious 
purpose, dignity, logical 
organization, length," whereas the informal essay is characterized by "the personal 
element (self-revelation, 
individual tastes and experiences, confidential manner), humor, graceful style, 
rambling structure, unconventionality 
or novelty of theme," etc.[1]"""

d = {}      # creating empty dic      
words = s.split() # spliting string and stroing in list
for word in words:
    new_word = ''
    for c in word:
        if c.isalnum(): # checking if indiviual chr is alphanumeric or not
            new_word = new_word + c
    print(new_word, end=' ')
    # if new_word not in d:
    #     d[new_word] = 1
    # else:
    #     d[new_word] = d[new_word] +1
print(d)

この回答が役に立ったら評価してください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.