ลบอักขระยกเว้นตัวเลขออกจากสตริงโดยใช้ Python?


142

ฉันจะลบอักขระทั้งหมดยกเว้นตัวเลขออกจากสตริงได้อย่างไร


@ Jan Tojnar: ยกตัวอย่างได้ไหม?
João Silva

@JG: ฉันมี gtk.Entry () และฉันต้องการคูณลอยที่ป้อนเข้าไป
ม.ค. Tojnar

1
@JanTojnar ใช้วิธี re.sub ตามคำตอบที่สองและระบุรายการอักขระที่จะเก็บไว้อย่างชัดเจนเช่น re.sub ("[^ 0123456789 \.]", "", "poo123.4and5fish")
Roger Heathcote

คำตอบ:


112

ใน Python 2. * วิธีที่เร็วที่สุดคือ.translateวิธีการ:

>>> x='aaa12333bb445bb54b5b52'
>>> import string
>>> all=string.maketrans('','')
>>> nodigs=all.translate(all, string.digits)
>>> x.translate(all, nodigs)
'1233344554552'
>>> 

string.maketransสร้างตารางการแปล (สตริงความยาว 256) ซึ่งในกรณีนี้จะเหมือนกับ''.join(chr(x) for x in range(256))(เร็วกว่าที่จะทำ ;-) .translateใช้ตารางการแปล (ซึ่งตรงนี้ไม่เกี่ยวข้องเนื่องจากallโดยพื้นฐานแล้วหมายถึงเอกลักษณ์) และลบอักขระที่มีอยู่ในอาร์กิวเมนต์ที่สอง - ส่วนสำคัญ

.translateการทำงานแตกต่างกันมากในสาย Unicode (และสตริงในหลาม 3 - ฉันทำคำถามปรารถนาระบุที่สำคัญการเปิดตัวของงูหลามเป็นที่น่าสนใจ!) - ไม่ได้ค่อนข้างง่ายนี้ไม่ได้ค่อนข้างเร็วนี้แม้ว่าจะยังคงใช้งานได้ค่อนข้าง

กลับไปที่ 2. * ประสิทธิภาพที่แตกต่างนั้นน่าประทับใจ ... :

$ python -mtimeit -s'import string; all=string.maketrans("", ""); nodig=all.translate(all, string.digits); x="aaa12333bb445bb54b5b52"' 'x.translate(all, nodig)'
1000000 loops, best of 3: 1.04 usec per loop
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 7.9 usec per loop

การเร่งให้เร็วขึ้น 7-8 เท่าแทบจะไม่ใช่ถั่วลิสงดังนั้นtranslateวิธีนี้จึงคุ้มค่าที่จะรู้และใช้ แนวทางอื่นที่ไม่ใช่ RE ยอดนิยม ... :

$ python -mtimeit -s'x="aaa12333bb445bb54b5b52"' '"".join(i for i in x if i.isdigit())'
100000 loops, best of 3: 11.5 usec per loop

ช้ากว่า RE 50% ดังนั้น.translateแนวทางจึงเต้นตามลำดับขนาด

ใน Python 3 หรือสำหรับ Unicode คุณต้องส่งผ่าน.translateการแมป (โดยมีลำดับไม่ใช่อักขระโดยตรงเป็นคีย์) ที่ส่งคืนNoneสิ่งที่คุณต้องการลบ นี่เป็นวิธีที่สะดวกในการแสดงสิ่งนี้สำหรับการลบ "ทุกอย่างยกเว้น" อักขระเพียงไม่กี่ตัว:

import string

class Del:
  def __init__(self, keep=string.digits):
    self.comp = dict((ord(c),c) for c in keep)
  def __getitem__(self, k):
    return self.comp.get(k)

DD = Del()

x='aaa12333bb445bb54b5b52'
x.translate(DD)

'1233344554552'ยังส่งเสียง อย่างไรก็ตามการใส่สิ่งนี้ใน xx.py เรามี ... :

$ python3.1 -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 8.43 usec per loop
$ python3.1 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
10000 loops, best of 3: 24.3 usec per loop

... ซึ่งแสดงให้เห็นถึงความได้เปรียบด้านประสิทธิภาพจะหายไปสำหรับงาน "การลบ" ประเภทนี้และจะทำให้ประสิทธิภาพลดลง


1
@sunqiang ใช่แน่นอน - มีเหตุผลที่ Py3k ไป Unicode เป็นประเภทสตริงข้อความแทนที่จะเป็นสตริงไบต์เช่นเดียวกับใน Py2 - เหตุผลเดียวกันกับที่ Java และ C # มี "string หมายถึง unicode" เหมือนกันเสมอ ... ค่าใช้จ่ายบางอย่างอาจจะ แต่การสนับสนุนที่ดีกว่าสำหรับทุกอย่างยกเว้นภาษาอังกฤษ! -)
Alex Martelli

29
x.translate(None, string.digits)ได้ผลลัพธ์จริง'aaabbbbbb'ซึ่งตรงกันข้ามกับสิ่งที่ตั้งใจไว้
Tom Dalling

4
การสะท้อนความคิดเห็นจาก Tom Dalling ตัวอย่างแรกของคุณจะเก็บตัวละครที่ไม่พึงปรารถนาทั้งหมดไว้ - ทำสิ่งที่ตรงกันข้าม
Chris Johnson

3
@ RyanB.Lynch et al ข้อผิดพลาดเกิดจากตัวแก้ไขในภายหลังและผู้ใช้อีกสองคนที่อนุมัติการแก้ไขดังกล่าวซึ่งในความเป็นจริงมันผิดทั้งหมด เปลี่ยนกลับแล้ว
Nick T

2
การลบล้างallbuiltin ... ไม่แน่ใจเกี่ยวกับเรื่องนั้น!
Andy Hayden

202

ใช้re.subดังนี้:

>>> import re
>>> re.sub('\D', '', 'aas30dsa20')
'3020'

\D จับคู่อักขระที่ไม่ใช่ตัวเลขดังนั้นโค้ดด้านบนจึงแทนที่อักขระที่ไม่ใช่ตัวเลขทุกตัวสำหรับสตริงว่าง

หรือคุณสามารถใช้filterเช่นนี้ (ใน Python 2):

>>> filter(str.isdigit, 'aas30dsa20')
'3020'

เนื่องจากใน Python 3 ให้filterส่งคืนตัววนซ้ำแทน a listคุณสามารถใช้สิ่งต่อไปนี้แทน:

>>> ''.join(filter(str.isdigit, 'aas30dsa20'))
'3020'

re เป็นสิ่งที่ชั่วร้ายในงานง่ายๆเช่นนี้อย่างที่สองคือสิ่งที่ดีที่สุดที่ฉันคิดสาเหตุ 'is ...
f0b0s

ตัวอย่างตัวกรองของคุณ จำกัด ไว้ที่ py2k
SilentGhost

2
@ f0b0s-iu9-info: คุณหมดเวลาหรือยัง บนเครื่องของฉัน (py3k) re เร็วกว่าตัวกรองสองเท่าด้วยisdigitเครื่องกำเนิดไฟฟ้าisdigtอยู่กึ่งกลางระหว่างพวกเขา
SilentGhost

@SilentGhost: ขอบคุณฉันใช้ IDLE จาก py2k ได้รับการแก้ไขแล้ว
João Silva

1
@asmaier เพียงใช้rสำหรับสตริงดิบ:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers

66
s=''.join(i for i in s if i.isdigit())

ตัวแปรเครื่องกำเนิดไฟฟ้าอื่น


ฆ่ามัน .. +1 จะดีกว่านี้ถ้าใช้ lamda
Barath Ravikumar

หากคุณต้องการรวมอักขระที่กำหนดเองเช่นรวมเชิงลบหรือทศนิยมให้ทำดังนี้s = ''.join(i for i in s if i.isdigit() or i in '-./\\')
ยูจีนชาบานอฟ

17

คุณสามารถใช้ตัวกรอง:

filter(lambda x: x.isdigit(), "dasdasd2313dsa")

ใน python3.0 คุณต้องเข้าร่วมสิ่งนี้ (ค่อนข้างน่าเกลียด :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))

เฉพาะใน py2k ใน py3k จะส่งคืนเครื่องกำเนิดไฟฟ้า
SilentGhost

แปลงstrเพื่อlistให้แน่ใจว่าใช้งานได้ทั้ง py2 และ py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.

13

ตามแนวของคำตอบของไบเออร์:

''.join(i for i in s if i.isdigit())

ไม่สิ่งนี้จะใช้ไม่ได้กับจำนวนลบเนื่องจาก-ไม่ใช่ตัวเลข
Oli

12

คุณสามารถทำได้อย่างง่ายดายโดยใช้ Regex

>>> import re
>>> re.sub("\D","","£70,000")
70000

วิธีที่ง่ายที่สุด
Iorek

6
สิ่งนี้แตกต่างจากคำตอบของJoão Silva ซึ่งให้ไว้เมื่อ 7 ปีก่อนอย่างไร
jww

7
x.translate(None, string.digits)

จะลบตัวเลขทั้งหมดออกจากสตริง ในการลบตัวอักษรและเก็บตัวเลขให้ทำดังนี้:

x.translate(None, string.letters)

3
ฉันได้รับ a TypeError: translate () รับอาร์กิวเมนต์เดียว (ให้ 2 ตัว) ทำไมคำถามนี้ถึงได้รับการโหวตในสถานะปัจจุบันจึงค่อนข้างน่าหงุดหงิด
Bobort

แปลเปลี่ยนจาก python 2 เป็น 3 ไวยากรณ์ที่ใช้วิธีนี้ใน python 3 คือ x.translate (str.maketrans ('', '', string.digits)) และ x.translate (str.maketrans ('', '' , string.ascii_letters)) ทั้งสองแถบนี้ไม่มีพื้นที่สีขาว ฉันจะไม่แนะนำแนวทางนี้อีกต่อไปแล้ว ...
ZaxR

6

ฝ่ายปฏิบัติการกล่าวถึงในความคิดเห็นว่าเขาต้องการคงตำแหน่งทศนิยมไว้ ซึ่งสามารถทำได้ด้วยเมธอด re.sub (ตามคำตอบที่สองและ IMHO ที่ดีที่สุด) โดยระบุตัวละครที่จะเก็บไว้อย่างชัดเจนเช่น

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'

"poo123.4and.5fish" ล่ะ?
ม.ค. Tojnar

ในรหัสของฉันฉันตรวจสอบจำนวนช่วงเวลาในสตริงอินพุตและเพิ่มข้อผิดพลาดหากมากกว่า 1
Roger Heathcote

4

เวอร์ชันที่รวดเร็วสำหรับ Python 3:

# xx3.py
from collections import defaultdict
import string
_NoneType = type(None)

def keeper(keep):
    table = defaultdict(_NoneType)
    table.update({ord(c): c for c in keep})
    return table

digit_keeper = keeper(string.digits)

นี่คือการเปรียบเทียบประสิทธิภาพกับ regex:

$ python3.3 -mtimeit -s'import xx3; x="aaa12333bb445bb54b5b52"' 'x.translate(xx3.digit_keeper)'
1000000 loops, best of 3: 1.02 usec per loop
$ python3.3 -mtimeit -s'import re; r = re.compile(r"\D"); x="aaa12333bb445bb54b5b52"' 'r.sub("", x)'
100000 loops, best of 3: 3.43 usec per loop

ดังนั้นฉันจึงเร็วกว่า regex 3 เท่าเล็กน้อย นอกจากนี้ยังเร็วกว่าclass Delด้านบนเนื่องจากdefaultdictการค้นหาทั้งหมดเป็นภาษา C แทนที่จะเป็น Python (ช้า) นี่คือเวอร์ชันในระบบเดียวกันของฉันสำหรับการเปรียบเทียบ

$ python3.3 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
100000 loops, best of 3: 13.6 usec per loop


2

น่าเกลียด แต่ใช้งานได้:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>

ทำไมคุณทำlist(s)?
SilentGhost

@SilentGhost มันเป็นความเข้าใจผิดของฉัน ได้รับการแก้ไขขอบคุณ :)
Gant

จริงๆแล้วด้วยวิธีนี้ฉันไม่คิดว่าคุณจะต้องใช้ "join" filter(lambda x: x.isdigit(), s)ทำงานได้ดีสำหรับฉัน ... โอ้เป็นเพราะฉันใช้ Python 2.7
Bobort

2

คุณสามารถอ่านอักขระแต่ละตัว ถ้าเป็นตัวเลขให้รวมไว้ในคำตอบ str.isdigit() วิธีเป็นวิธีที่จะรู้ว่าตัวละครเป็นหลัก

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'

สิ่งนี้แตกต่างจากคำตอบของ f0b0s อย่างไร คุณควรแก้ไขคำตอบนั้นแทนหากคุณมีข้อมูลเพิ่มเติมที่จะนำมา
chevybow

1
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 ลูปดีที่สุดคือ 3: 2.48 usec ต่อลูป

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 ลูปที่ดีที่สุดคือ 3: 2.02 usec ต่อลูป

$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 ลูปดีที่สุดคือ 3: 2.37 usec ต่อลูป

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 ลูปที่ดีที่สุดคือ 3: 1.97 usec ต่อลูป

ฉันสังเกตว่าการเข้าร่วมนั้นเร็วกว่าย่อย


ทำไมคุณถึงทำสองวิธีนี้ซ้ำสองครั้ง? และคุณช่วยอธิบายได้ไหมว่าคำตอบของคุณแตกต่างจากคำตอบที่ยอมรับอย่างไร
ม.ค. Tojnar

ผลลัพธ์ทั้งสองได้ผลลัพธ์เดียวกัน แต่ฉันแค่อยากจะแสดงให้เห็นว่าการเข้าร่วมนั้นเร็วกว่าวิธีย่อยในผลลัพธ์
AnilReddy

พวกเขาไม่ได้รหัสของคุณจะตรงกันข้าม และคุณมีสี่การวัด แต่มีเพียงสองวิธีเท่านั้น
ม.ค. Tojnar

0

ไม่ใช่ซับเดียว แต่ง่ายมาก:

buffer = ""
some_str = "aas30dsa20"

for char in some_str:
    if not char.isdigit():
        buffer += char

print( buffer )

0

ฉันใช้สิ่งนี้ 'letters'ควรมีตัวอักษรทั้งหมดที่คุณต้องการกำจัด:

Output = Input.translate({ord(i): None for i in 'letters'}))

ตัวอย่าง:

Input = "I would like 20 dollars for that suit" Output = Input.translate({ord(i): None for i in 'abcdefghijklmnopqrstuvwxzy'})) print(Output)

เอาท์พุต: 20


0
my_string="sdfsdfsdfsfsdf353dsg345435sdfs525436654.dgg(" 
my_string=''.join((ch if ch in '0123456789' else '') for ch in my_string)
print(output:+my_string)

เอาต์พุต: 353345435525436654

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.