Melucuti semuanya kecuali karakter alfanumerik dari string dengan Python

339

Apa cara terbaik untuk menghapus semua karakter non alfanumerik dari sebuah string, menggunakan Python?

Solusi yang disajikan dalam varian PHP dari pertanyaan ini mungkin akan bekerja dengan beberapa penyesuaian kecil, tetapi tampaknya tidak terlalu 'pythonic' bagi saya.

Sebagai catatan, saya tidak hanya ingin menghapus titik dan koma (dan tanda baca lainnya), tetapi juga mengutip, tanda kurung, dll.

Mark van Prapaskah
sumber
8
Apakah Anda peduli dengan karakter alfanumerik internasional, seperti 'æøå', 'مرحبا', 'สวัสดี', 'こ ん に ち は'?
Pimin Konstantin Kefaloukos
4
@PiminKonstantinKefaloukos Ya saya peduli dengan karakter internasional, maka komentar saya tentang jawaban yang diterima untuk menggunakan re.UNICODE.
Mark van Lent

Jawaban:

337

Saya hanya menghitung waktu beberapa fungsi karena penasaran. Dalam tes ini saya menghapus karakter non-alfanumerik dari string string.printable(bagian dari stringmodul bawaan). Penggunaan dikompilasi '[\W_]+'dan pattern.sub('', str)ditemukan paling cepat.

$ 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
Otto Allmendinger
sumber
2
Hasil yang sangat menarik: Saya berharap ekspresi reguler menjadi lebih lambat. Menariknya, saya mencoba ini dengan satu opsi lain ( valid_characters = string.ascii_letters + string.digitsdiikuti oleh join(ch for ch in string.printable if ch in valid_characters)dan itu 6 mikrodetik lebih cepat dari isalnum()opsi. Masih jauh lebih lambat daripada regexp sekalipun.
DrAl
+1, mengukur waktu itu bagus! (tetapi dalam kedua dari belakang, lakukan pattern.sub('', string.printable)sebaliknya - konyol untuk memanggil re.sub ketika Anda memiliki objek RE! -).
Alex Martelli
46
Sebagai catatan: gunakan re.compile('[\W_]+', re.UNICODE)untuk membuatnya unicode aman.
Mark van Lent
3
bagaimana Anda melakukannya tanpa menghilangkan ruang putih?
maudulus
6
lakukan tanpa menghilangkan spasi putih: re.sub ('[\ W _] +', '', kalimat, flags = re.UNICODE)
PALEN
269

Ekspresi reguler untuk penyelamatan:

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

Dengan definisi Python '\W== [^a-zA-Z0-9_], yang mengecualikan semua numbers, lettersdan_

Semut Aasma
sumber
2
Apa yang dilakukan tanda tambah di regexp? (Saya tahu apa artinya, hanya ingin tahu mengapa itu diperlukan untuk ulang.)
Mark van Lent
7
@ Mark: Saya membayangkan itu akan mempercepat substitusi karena penggantiannya akan menghilangkan semua karakter non-kata dalam satu blok sekaligus, daripada menghapusnya satu per satu.
DrAl
2
Ya, saya membuat bangku sementara menyetel beberapa kode kinerja kritis beberapa waktu lalu. Jika ada rentang karakter yang signifikan untuk menggantikan speedup sangat besar.
Ants Aasma
20
Ini mungkin tidak relevan dalam kasus ini, tetapi \Wakan tetap menggarisbawahi juga.
Blixt
12
Mengikuti tip @Blixt, jika Anda hanya menginginkan huruf dan angka, Anda dapat melakukan re.sub (r '[^ a-zA-Z0-9]', '', your_string)
Nigini
69

Gunakan metode str.translate () .

Anggap Anda akan sering melakukan ini:

(1) Sekali, buat string yang berisi semua karakter yang ingin Anda hapus:

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

(2) Setiap kali Anda ingin mengikis string:

scrunched = s.translate(None, delchars)

Biaya setup mungkin lebih baik dibandingkan dengan kompilasi ulang; biaya marjinal jauh lebih rendah:

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

Catatan: Menggunakan string.printable sebagai data benchmark memberikan pola '[\ W _] +' keuntungan yang tidak adil ; semua karakter non-alfanumerik berada dalam satu tandan ... dalam data biasa akan ada lebih dari satu penggantian yang harus dilakukan:

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

Inilah yang terjadi jika Anda memberi sedikit lebih banyak pekerjaan untuk dilakukan:

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
John Machin
sumber
1
Menggunakan terjemahan memang sedikit lebih cepat. Bahkan ketika menambahkan for for tepat sebelum melakukan subtitusi / terjemahan (untuk membuat biaya setup menjadi lebih sedikit) masih membuat terjemahan kira-kira 17 kali lebih cepat daripada regexp pada mesin saya. Senang mendengarnya.
Mark van Lent
3
Ini jelas merupakan solusi yang paling pythonic.
codygman
1
Ini hampir meyakinkan saya, tapi saya sarankan menggunakan string.punctuationAlih-alih''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols
1
Perhatikan bahwa ini berfungsi untuk strobjek tetapi bukan unicodeobjek.
Yavar
@ John Machin Apakah itu pada dasarnya pemahaman daftar yang diteruskan sebagai argumen .join()?
AdjunctProfessorFalcon
42

Kamu bisa mencoba:

print ''.join(ch for ch in some_string if ch.isalnum())
ars
sumber
16
>>> import re
>>> string = "Kl13@£$%[};'\""
>>> pattern = re.compile('\W')
>>> string = re.sub(pattern, '', string)
>>> print string
Kl13
DisusterAussie
sumber
1
saya menyukai jawaban Anda tetapi itu menghilangkan karakter Arab juga dapatkah Anda memberi tahu saya cara menyimpannya
Charif DZ
14

Bagaimana tentang:

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

Ini bekerja dengan menggunakan pemahaman daftar untuk menghasilkan daftar karakter InputStringjika mereka hadir dalam gabungan ascii_lettersdan digitsstring. Kemudian bergabung daftar menjadi string.

DrAl
sumber
Tampaknya string.ascii_letters hanya berisi huruf (duh) dan bukan angka. Saya juga perlu nomor ...
Mark van Lent
Menambahkan string.digits memang akan menyelesaikan masalah yang baru saja saya sebutkan. :)
Mark van Lent
Ya, saya menyadari bahwa ketika saya kembali untuk membaca pertanyaan Anda. Catatan untuk diri sendiri: belajar membaca!
DrAl
5

Sebagai gantinya dari beberapa jawaban lain di sini, saya menawarkan cara yang sangat sederhana dan fleksibel untuk mendefinisikan sekumpulan karakter yang Anda ingin membatasi konten string. Dalam hal ini, saya mengizinkan tanda alfanumerik PLUS dan garis bawah. Cukup tambahkan atau hapus karakter dari saya PERMITTED_CHARSsesuai kasus penggunaan Anda.

PERMITTED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" 
someString = "".join(c for c in someString if c in PERMITTED_CHARS)
Terima kasih
sumber
3
Alih-alih meng-hardcoding karakter yang diizinkan, yang rawan kesalahan halus, gunakan string.digits + string.ascii_letters + '_-'.
Reti43
Saran Anda tidak salah, tetapi juga tidak menyimpan banyak karakter "mengetik" jika itu adalah tujuan Anda. Jika Anda menyalin posting saya, Anda juga tidak akan salah ketik! Namun, poin sebenarnya dari jawaban saya adalah memungkinkan sarana eksplisit, terbuka dan sederhana untuk menentukan dengan tepat karakter yang ingin Anda izinkan.
BuvinJ
Sebagai jalan tengah, Anda dapat menggabungkan saran-saran ini ke dalam SPECIAL_CHARS = '_-'dan kemudian menggunakanstring.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ
Itu adalah saran dalam hal apa yang masuk akal, kecuali kita melakukan golf kode. "Berjalan" di sekitar keyboard untuk mengetik 52 huruf alfabet secara berurutan membutuhkan waktu lebih lama daripada mengimpor paket untuk menggunakan satu atau dua objek. Dan itu tidak termasuk waktu untuk memeriksa ulang apakah Anda mengetik semuanya dengan benar. Ini tentang praktik yang baik, itu saja.
Reti43
Aku mendengarmu! Maksud saya sebenarnya di sini adalah fleksibilitas ekstrim, jika Anda ingin lebih spesifik dengan set karakter Anda.
BuvinJ
5
sent = "".join(e for e in sent if e.isalpha())
Tom Kalvijn
sumber
Saya akan mencoba menjelaskan: ia melewati semua karakter string e for e in sentdan memeriksa melalui if e.isalpha()pernyataan jika karakter saat ini adalah simbol alfabet, jika demikian - bergabung dengan sentvariabel melalui sent = "".join()dan semua simbol non-alfabet akan diganti dengan ""(string kosong) karena dari joinfungsi.
Sysanin
karena ini melakukan loop per karakter daripada mengandalkan C regex, bukankah ini sangat lambat?
dcsan
3
for char in my_string:
    if not char.isalnum():
        my_string = my_string.replace(char,"")
Junior Ogun
sumber
2

Pengaturan waktu dengan string acak dari printable 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()

Hasil (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.maketrans& str.translatetercepat, tetapi termasuk semua karakter non-ASCII. re.compile& pattern.sublebih lambat, tetapi entah bagaimana lebih cepat dari ''.join& filter.

Solomon Ucko
sumber
-1

Jika saya mengerti dengan benar cara termudah adalah dengan menggunakan ekspresi reguler karena memberikan Anda banyak fleksibilitas tetapi metode sederhana lainnya adalah menggunakan untuk loop berikut adalah kode dengan contoh saya juga menghitung kemunculan kata dan disimpan dalam kamus ..

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)

nilai ini jika jawaban ini berguna!

Abhishek Pratap Singh
sumber