การปอกทุกอย่างยกเว้นตัวอักษรและตัวเลขจากสตริงใน Python


337

วิธีที่ดีที่สุดในการตัดอักขระที่ไม่ใช่ตัวอักษรและตัวเลขทั้งหมดจากสตริงโดยใช้ Python คืออะไร

คำตอบที่นำเสนอในตัวแปร PHP ของคำถามนี้อาจใช้งานได้กับการปรับเปลี่ยนเล็กน้อย แต่ดูเหมือนจะไม่ 'pythonic' มากสำหรับฉัน

สำหรับบันทึกฉันไม่เพียง แต่ต้องการตัดเครื่องหมายจุดและเครื่องหมายจุลภาค (และเครื่องหมายวรรคตอนอื่น ๆ ) แต่ยังเสนอราคาเครื่องหมายวงเล็บ ฯลฯ


7
คุณใส่ใจกับตัวอักษรและตัวเลขระหว่างประเทศเช่น 'æøå', 'مرحبا', 'สวัสดี', 'こんにちは' หรือไม่?
Pimin Konstantin Kefaloukos

4
@PiminKonstantinKefaloukos ใช่ฉันสนใจเกี่ยวกับตัวอักษรต่างประเทศดังนั้นความคิดเห็นของฉันเกี่ยวกับคำตอบที่ยอมรับเพื่อใช้ re.UNICODE
Mark van Lent

คำตอบ:


336

ฉันเพิ่งหมดเวลาฟังก์ชั่นบางอย่างจากความอยากรู้ ในการทดสอบเหล่านี้ฉันกำลังลบอักขระที่ไม่ใช่ตัวอักษรและตัวเลขออกจากสตริง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
ผลลัพธ์ที่น่าสนใจ: ฉันคาดว่านิพจน์ทั่วไปจะช้าลง ที่น่าสนใจฉันพยายามนี้มีตัวเลือกอีกคนหนึ่ง ( valid_characters = string.ascii_letters + string.digitsตามด้วยjoin(ch for ch in string.printable if ch in valid_characters)และมันก็เป็น 6 microseconds เร็วกว่าisalnum()ตัวเลือกยังคงช้ากว่า regexp แม้ว่า..
DrAl

+1 เวลาการวัดดี! (แต่ในขั้นสุดท้ายให้ทำpattern.sub('', string.printable)แทน - โง่โทร re.sub เมื่อคุณมีวัตถุ RE! -)
Alex Martelli

46
สำหรับบันทึก: ใช้re.compile('[\W_]+', re.UNICODE)เพื่อทำให้ยูนิโค้ดปลอดภัย
Mark van Lent

3
คุณจะทำอย่างไรโดยไม่ลบช่องว่างออก?
maudulus

6
ทำโดยไม่ลบช่องว่าง: re.sub ('[\ W _] +', '', ประโยค, ธง = re.UNICODE)
PALEN

268

นิพจน์ทั่วไปเพื่อช่วยเหลือ:

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

โดยงูหลามนิยาม'\W== [^a-zA-Z0-9_]ซึ่งไม่รวมทั้งหมดnumbers, lettersและ_


2
เครื่องหมายบวกทำอะไรใน regexp (ฉันรู้ว่ามันหมายถึงเพียงแค่อยากรู้ว่าทำไมมันจำเป็นสำหรับ re.sub ได้.)
มาร์คฟานเข้าพรรษา

7
@ Mark: ฉันคิดว่ามันจะเพิ่มความเร็วในการทดแทนเนื่องจากการแทนที่จะกำจัดอักขระที่ไม่ใช่คำทั้งหมดในบล็อกในคราวเดียวแทนที่จะลบทีละตัว
DrAl

2
ใช่ฉัน benched ว่าในขณะที่การปรับแต่งรหัสที่สำคัญประสิทธิภาพในขณะที่ที่ผ่านมา หากมีช่วงของตัวละครสำคัญที่จะมาแทนที่การเร่งความเร็วมีขนาดใหญ่มาก
มด Aasma

20
มันอาจจะไม่เกี่ยวข้องในกรณีนี้ แต่\Wจะรักษาขีดเส้นใต้ไว้เช่นกัน
Blixt

12
การติดตาม @Blixt เคล็ดลับหากคุณต้องการเพียงตัวอักษรและตัวเลขคุณสามารถทำ re.sub (r '[^ a-zA-Z0-9]', '', your_string)
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 _] +' ได้เปรียบที่ไม่เป็นธรรม ; อักขระที่ไม่ใช่ตัวอักษรและตัวเลขทั้งหมดอยู่ในกลุ่มเดียว ... ในข้อมูลทั่วไปจะมีการแทนที่มากกว่าหนึ่งรายการที่ต้องทำ:

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
การใช้การแปลนั้นเร็วขึ้นเล็กน้อย แม้ในขณะที่เพิ่มสำหรับลูปที่ถูกต้องก่อนที่จะทำการทดแทน / การแปล (เพื่อให้การตั้งค่ามีน้ำหนักน้อยลง) ยังทำให้การแปลประมาณ 17 ครั้งเร็วกว่า regexp บนเครื่องของฉัน ดีแล้วที่รู้.
Mark van Lent

3
นี่เป็นวิธีการแก้ปัญหา pythonic ที่แน่นอนที่สุด
codygman

1
สิ่งนี้เกือบจะโน้มน้าวใจฉัน แต่ฉันขอแนะนำให้ใช้string.punctuationแทน''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols

1
โปรดทราบว่าสิ่งนี้ใช้ได้กับstrวัตถุ แต่ไม่ใช่unicodeวัตถุ
Yavar

@John Machin นั่นคือความเข้าใจในรายการที่ถูกส่งผ่านเป็นอาร์กิวเมนต์.join()หรือไม่?
AdjunctProfessorFalcon


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)])

สิ่งนี้ทำงานได้โดยใช้ list comprehension เพื่อสร้างรายการของอักขระในInputStringกรณีที่มีอยู่ในการรวมascii_lettersและdigitsสตริง จากนั้นรวมรายการเข้าด้วยกันเป็นสตริง


ดูเหมือนว่า string.ascii_letters มีเพียงตัวอักษร (duh) และไม่ใช่ตัวเลข ฉันยังต้องตัวเลข ...
มาร์คฟานเข้าพรรษา

การเพิ่ม string.digits จะแก้ปัญหาที่ฉันเพิ่งพูดถึง :)
มาร์คแวนเข้าพรรษา

ใช่ฉันรู้ว่าเมื่อฉันกลับไปอ่านคำถามของคุณ หมายเหตุถึงตนเอง: เรียนรู้ที่จะอ่าน!
DrAl

4

จากคำตอบอื่นที่นี่ฉันขอเสนอวิธีที่ง่ายและยืดหยุ่นในการกำหนดชุดอักขระที่คุณต้องการ จำกัด เนื้อหาของสตริง ในกรณีนี้ฉันอนุญาตให้ใช้ตัวอักษรและตัวเลขและเครื่องหมายขีดเส้นใต้ เพียงเพิ่มหรือลบตัวละครจากฉันPERMITTED_CHARSตามความเหมาะสมกับการใช้งานของคุณ

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

2
แทนที่จะ hardcoding string.digits + string.ascii_letters + '_-'ตัวละครที่ได้รับอนุญาตซึ่งมีแนวโน้มที่จะเกิดข้อผิดพลาดที่ลึกซึ้งใช้
Reti43

ข้อเสนอแนะของคุณไม่ผิด แต่มันก็ไม่ได้ช่วยประหยัดอักขระจำนวนมากในการ "พิมพ์" หากนั่นคือเป้าหมายของคุณ หากคุณคัดลอกโพสต์ของฉันคุณก็จะไม่มีการพิมพ์ผิด! อย่างไรก็ตามจุดที่แท้จริงของคำตอบของฉันคืออนุญาตให้มีวิธีการที่ชัดเจนเปิดเผยและเรียบง่ายในการกำหนดตัวอักษรที่คุณต้องการอนุญาตอย่างชัดเจน
BuvinJ

ในฐานะที่เป็นพื้นกลางคุณอาจรวมข้อเสนอแนะเหล่านี้เข้าด้วยกันSPECIAL_CHARS = '_-'แล้วใช้string.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ

มันเป็นข้อเสนอแนะในแง่ของสิ่งที่สมเหตุสมผลเว้นแต่ว่าเรากำลังทำรหัสกอล์ฟ "การเดิน" รอบ ๆ คีย์บอร์ดเพื่อพิมพ์ตัวอักษร 52 ตัวเพื่อให้ใช้เวลานานกว่าการนำเข้าแพคเกจเพื่อใช้วัตถุหนึ่งหรือสองตัว และนั่นไม่รวมเวลาในการตรวจสอบอีกครั้งว่าคุณพิมพ์ถูกต้องแล้ว มันเกี่ยวกับแนวทางปฏิบัติที่ดีนั่นคือทั้งหมดที่
Reti43

ฉันได้ยินคุณ! จุดที่แท้จริงของฉันในที่นี้คือความยืดหยุ่นอย่างมากในกรณีที่คุณต้องการให้เฉพาะเจาะจงมากขึ้นกับชุดตัวละครของคุณ
BuvinJ

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 regex นี่มันช้ามากหรือเปล่า
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.maketrans& str.translateเร็วที่สุด แต่มีอักขระที่ไม่ใช่ ASCII ทั้งหมด re.compileและpattern.subจะช้า แต่อย่างใดเร็วกว่าและ''.joinfilter


-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.