Tước mọi thứ trừ ký tự chữ và số từ một chuỗi trong Python


336

Cách tốt nhất để loại bỏ tất cả các ký tự không chữ và số khỏi chuỗi, sử dụng Python là gì?

Các giải pháp được trình bày trong biến thể PHP của câu hỏi này có thể sẽ hoạt động với một số điều chỉnh nhỏ, nhưng đối với tôi có vẻ không 'pythonic'.

Đối với bản ghi, tôi không chỉ muốn xóa dấu chấm và dấu phẩy (và các dấu chấm câu khác), mà còn cả dấu ngoặc kép, dấu ngoặc, v.v.


7
Bạn có quan tâm đến các ký tự chữ và số quốc tế, như 'æøå', 'مرحبا', 'สวัสดี', 'ん に ち'?
Pimin Konstantin Kefaloukos

4
@PiminKonstantinKefaloukos Có Tôi quan tâm đến các ký tự quốc tế, do đó nhận xét của tôi về câu trả lời được chấp nhận để sử dụng re.UNICODE.
Mark van Chay

Câu trả lời:


335

Tôi chỉ hẹn giờ một số chức năng vì tò mò. Trong các thử nghiệm này, tôi sẽ loại bỏ các ký tự không chữ và số khỏi chuỗi string.printable(một phần của stringmô-đun tích hợp). Việc sử dụng biên dịch '[\W_]+'pattern.sub('', str)được tìm thấy là nhanh nhất.

$ 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
Kết quả rất thú vị: Tôi đã dự kiến ​​các biểu thức chính quy sẽ chậm hơn. Thật thú vị, tôi đã thử điều này với một tùy chọn khác ( valid_characters = string.ascii_letters + string.digitstiếp theo join(ch for ch in string.printable if ch in valid_characters)và nó nhanh hơn 6 micro giây so với isalnum()tùy chọn. Tuy nhiên, vẫn chậm hơn nhiều so với
regrec

+1, đo thời gian là tốt! (nhưng trong áp chót, pattern.sub('', string.printable)thay vào đó - hãy ngớ ngẩn gọi re.sub khi bạn có đối tượng RE! -).
Alex Martelli

46
Đối với bản ghi: sử dụng re.compile('[\W_]+', re.UNICODE)để làm cho nó unicode an toàn.
Mark van Chay

3
Làm thế nào để bạn làm điều đó mà không loại bỏ khoảng trắng?
maudulus

6
làm điều đó mà không xóa khoảng trắng: re.sub ('[\ W _] +', '', câu, flags = re.UNICODE)
PALEN

267

Biểu thức thường xuyên để giải cứu:

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

Bởi Python nét '\W== [^a-zA-Z0-9_], mà không bao gồm tất cả numbers, letters_


2
Dấu cộng làm gì trong regrec? (Tôi biết ý nghĩa của nó, chỉ tò mò về lý do tại sao nó cần cho re.sub.)
Mark van Lent

7
@Mark: Tôi tưởng tượng nó sẽ tăng tốc độ thay thế vì sự thay thế sẽ loại bỏ tất cả các ký tự không phải từ trong một khối, thay vì loại bỏ từng ký tự một.
DrAl

2
Vâng, tôi đã chuẩn bị điều đó trong khi điều chỉnh một số mã quan trọng hiệu suất một thời gian trước đây. Nếu có các khoảng cách đáng kể của các ký tự để thay thế tốc độ là rất lớn.
Kiến Aasma

20
Nó có thể không liên quan trong trường hợp này, nhưng \Wcũng sẽ giữ dấu gạch dưới.
Blixt

12
Theo mẹo @Blixt, nếu bạn chỉ muốn chữ cái và số, bạn có thể thực hiện re.sub (r '[^ a-zA-Z0-9]', '', your_opes)
Nigini

68

Sử dụng phương thức str.translate () .

Giả sử bạn sẽ làm điều này thường xuyên:

(1) Một lần, tạo một chuỗi chứa tất cả các ký tự bạn muốn xóa:

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

(2) Bất cứ khi nào bạn muốn chà một chuỗi:

scrunched = s.translate(None, delchars)

Chi phí thiết lập có thể so sánh thuận lợi với re.compile; chi phí cận biên thấp hơn nhiều:

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

Lưu ý: Sử dụng chuỗi.printable làm dữ liệu điểm chuẩn mang lại cho mẫu '[\ W _] +' một lợi thế không công bằng ; tất cả các ký tự không chữ và số nằm trong một bó ... trong dữ liệu điển hình sẽ có nhiều hơn một thay thế để làm:

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

Đây là những gì sẽ xảy ra nếu bạn cho re.sub thêm một chút việc phải làm:

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
Sử dụng dịch thực sự là khá nhanh hơn một chút. Ngay cả khi thêm một vòng lặp for ngay trước khi thực hiện thay thế / dịch thuật (để làm cho chi phí thiết lập giảm đi ít hơn) vẫn khiến bản dịch nhanh hơn khoảng 17 lần so với regrec trên máy của tôi. Tốt để biết.
Đánh dấu cho mượn

3
Đây chắc chắn là giải pháp pythonic nhất.
codygman

1
Điều này gần như thuyết phục tôi, nhưng tôi sẽ đề nghị sử dụng string.punctuationThay vì''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols 14/03/2015

1
Lưu ý rằng điều này làm việc cho strcác đối tượng nhưng không phải unicodeđối tượng.
Yavar

@ John Machin Có phải đó thực chất là một sự hiểu biết danh sách đang được thông qua như là một đối số .join()?
AdjuncProfưởngFalcon

41

Bạn có thể thử:

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

Tôi yêu câu trả lời của bạn nhưng nó cũng xóa các ký tự tiếng Ả Rập, bạn có thể cho tôi biết cách giữ chúng không
Charif DZ

13

Làm thế nào về:

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

Điều này hoạt động bằng cách sử dụng khả năng hiểu danh sách để tạo danh sách các ký tự InputStringnếu chúng có mặt trong chuỗi kết hợp ascii_lettersdigitschuỗi. Sau đó, nó kết hợp danh sách lại với nhau thành một chuỗi.


Có vẻ như string.ascii_letters chỉ chứa các chữ cái (duh) chứ không chứa các số. Tôi cũng cần những con số ...
Mark van Lent

Thêm chuỗi.digits thực sự sẽ giải quyết vấn đề tôi vừa đề cập. :)
Đánh dấu vào

Vâng, tôi nhận ra rằng khi tôi quay lại để đọc câu hỏi của bạn. Lưu ý đến bản thân: học đọc!
DrAl

4

Như một câu trả lời từ một số câu trả lời khác ở đây, tôi đưa ra một cách thực sự đơn giản và linh hoạt để xác định một bộ ký tự mà bạn muốn giới hạn nội dung của một chuỗi. Trong trường hợp này, tôi đang cho phép chữ và số gạch ngang PLUS và gạch dưới. Chỉ cần thêm hoặc xóa các ký tự từ tôi PERMITTED_CHARSlà phù hợp với trường hợp sử dụng của bạn.

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

2
Thay vì mã hóa các ký tự được phép, dễ bị lỗi tinh vi, hãy sử dụng string.digits + string.ascii_letters + '_-'.
Reti43

Đề xuất của bạn không sai, nhưng nó cũng không lưu nhiều ký tự "gõ" nếu đó là mục tiêu của bạn. Nếu bạn sao chép bài viết của tôi, bạn cũng sẽ không mắc lỗi đánh máy! Tuy nhiên, điểm thực sự trong câu trả lời của tôi là cho phép một phương tiện rõ ràng, kết thúc mở và đơn giản để xác định chính xác những ký tự bạn muốn cho phép.
BuvinJ

Là một nền tảng trung gian, bạn có thể kết hợp các đề xuất này vào SPECIAL_CHARS = '_-'và sau đó sử dụngstring.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ

Đó là một gợi ý về những gì hợp lý, trừ khi chúng tôi đang chơi golf. "Đi bộ" quanh bàn phím để nhập 52 chữ cái trong bảng chữ cái mất nhiều thời gian hơn so với nhập một gói để sử dụng một hoặc hai đối tượng. Và điều đó không bao gồm thời gian để kiểm tra lại xem bạn đã nhập đúng chưa. Đó là về thực hành tốt, đó là tất cả.
Reti43

Tôi nghe bạn! Quan điểm thực sự của tôi ở đây là cực kỳ linh hoạt, trong trường hợp bạn muốn cụ thể hơn với bộ ký tự của mình.
BuvinJ

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

Tôi sẽ cố gắng giải thích: nó đi qua tất cả các ký tự chuỗi trong e for e in sentvà kiểm tra thông qua if e.isalpha()câu lệnh nếu char hiện tại là ký hiệu chữ cái, nếu vậy - nối nó với sentbiến qua sent = "".join()và tất cả các ký hiệu không phải là chữ cái sẽ được thay thế bằng ""(chuỗi rỗng) bởi vì của joinchức năng.
Sysanin

vì việc này đang thực hiện một vòng lặp cho mỗi ký tự thay vì dựa vào C regex, điều này có quá chậm không?
dcsan


2

Thời gian với các chuỗi ngẫu nhiên của các bản in 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()

Kết quả (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.translatelà nhanh nhất, nhưng bao gồm tất cả các ký tự không phải ASCII. re.compile& pattern.subchậm hơn, nhưng bằng cách nào đó nhanh hơn ''.join& filter.


-1

Nếu tôi hiểu chính xác thì cách dễ nhất là sử dụng biểu thức chính quy vì nó cung cấp cho bạn rất nhiều tính linh hoạt nhưng phương pháp đơn giản khác là sử dụng cho vòng lặp sau đây là mã với ví dụ tôi cũng đã đếm sự xuất hiện của từ và được lưu trữ trong từ điển ..

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)

vui lòng đánh giá điều này nếu câu trả lời này hữu ích!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.