Xóa các ký tự ngoại trừ các chữ số khỏi chuỗi bằng Python?


137

Làm thế nào tôi có thể loại bỏ tất cả các ký tự ngoại trừ số từ chuỗi?


@Jan Tojnar: Bạn có thể đưa ra một ví dụ?
João Silva

@JG: Tôi có gtk.Entry () và tôi muốn nhân số float vào đó.
Jan Tojnar

1
@JanTojnar sử dụng phương thức re.sub theo câu trả lời hai và liệt kê rõ ràng những ký tự cần giữ, ví dụ re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote

Câu trả lời:


112

Trong Python 2. *, cho đến nay, cách tiếp cận nhanh nhất là .translatephương thức:

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

string.maketranstạo một bảng dịch (một chuỗi có độ dài 256) mà trong trường hợp này giống như ''.join(chr(x) for x in range(256))(chỉ cần thực hiện nhanh hơn ;-). .translateáp dụng bảng dịch (ở đây không liên quan vì allvề cơ bản có nghĩa là danh tính) VÀ xóa các ký tự có trong đối số thứ hai - phần chính.

.translatehoạt động rất khác nhau trên các chuỗi Unicode (và các chuỗi trong Python 3 - Tôi thực sự muốn các câu hỏi được chỉ định phát hành chính của Python là gì!) - không hoàn toàn đơn giản, không hoàn toàn nhanh như vậy, mặc dù vẫn hoàn toàn có thể sử dụng được.

Quay lại 2. *, sự khác biệt về hiệu suất rất ấn tượng ...:

$ 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

Tăng tốc mọi thứ lên 7-8 lần hầu như không phải là đậu phộng, vì vậy translatephương pháp này rất đáng để biết và sử dụng. Cách tiếp cận phi RE phổ biến khác ...:

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

chậm hơn 50% so với RE, vì vậy .translatecách tiếp cận đánh bại nó bằng một mức độ lớn hơn.

Trong Python 3 hoặc Unicode, bạn cần truyền .translateánh xạ (với các lệnh, không phải ký tự trực tiếp, dưới dạng các khóa) trả vềNone những gì bạn muốn xóa. Đây là một cách thuận tiện để diễn đạt điều này để xóa "mọi thứ trừ" một vài ký tự:

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)

cũng phát ra '1233344554552' . Tuy nhiên, đặt cái này vào xx.py chúng ta có ...:

$ 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

... Điều này cho thấy lợi thế về hiệu suất sẽ biến mất, đối với loại nhiệm vụ "xóa" này và trở thành giảm hiệu suất.


1
@sunqiang, vâng, hoàn toàn - có một lý do Py3k đã chuyển sang Unicode dưới dạng kiểu chuỗi văn bản, thay vì chuỗi byte như trong Py2 - cùng lý do Java và C # luôn có cùng một "chuỗi nghĩa là unicode" ... một số chi phí, có thể, nhưng NHIỀU hỗ trợ tốt hơn cho bất cứ thứ gì trừ tiếng Anh! -).
Alex Martelli

29
x.translate(None, string.digits)thực sự dẫn đến kết quả 'aaabbbbbb'ngược lại với dự định.
Tom Dalling

4
Nhận lại những bình luận từ Tom Dalling, ví dụ đầu tiên của bạn giữ tất cả các nhân vật không mong muốn - thực hiện ngược lại với những gì bạn nói.
Chris Johnson

3
@ RyanB.Lynch et al, lỗi là do một biên tập viên sau đó và hai người dùng khác đã chấp thuận cho biết chỉnh sửa , trong thực tế, điều đó là hoàn toàn sai. Hoàn nguyên.
Nick T

1
ghi đè alldựng sẵn ... không chắc chắn về điều đó!
Andy Hayden

197

Sử dụng re.sub, như vậy:

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

\D khớp với bất kỳ ký tự không có chữ số nào, vì vậy, mã ở trên, về cơ bản thay thế mọi ký tự không có chữ số cho chuỗi trống.

Hoặc bạn có thể sử dụng filter, như vậy (trong Python 2):

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

Vì trong Python 3, filtertrả về một iterator thay vì mộtlist , bạn có thể sử dụng như sau:

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

re là xấu trong nhiệm vụ đơn giản như vậy, thứ hai là tốt nhất tôi nghĩ, vì phương pháp 'là ...' là nhanh nhất cho chuỗi.
f0b0

ví dụ về bộ lọc của bạn được giới hạn ở py2k
SilentGhost

2
@ f0b0s-iu9-thông tin: bạn đã hẹn giờ chưa? trên máy của tôi (py3k) nhanh hơn gấp đôi so với bộ lọc isdigit, trình tạo với isdigtmột nửa giữa chúng
SilentGhost

@SilentGhost: Cảm ơn, tôi đã sử dụng IDLE từ py2k. Bây giờ nó đã được sửa.
João Silva

1
@asmaier Đơn giản chỉ cần sử dụng rcho chuỗi thô:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers

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

Một biến thể máy phát điện khác.


Giết nó .. + 1 Sẽ còn tuyệt hơn nữa nếu lamda được sử dụng
Barath Ravikumar

17

Bạn có thể sử dụng bộ lọc:

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

Trên python3.0 bạn phải tham gia cái này (hơi xấu xí :()

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

chỉ trong py2k, trong py3k, nó trả về một trình tạo
SilentGhost

chuyển đổi strđể listđảm bảo nó hoạt động trên cả py2 và py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.

13

dọc theo câu trả lời của người bay:

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

Không, điều này sẽ không hoạt động đối với các số âm vì -không phải là một chữ số.
Oli

12

Bạn có thể dễ dàng làm điều đó bằng Regex

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

Cho đến nay cách dễ nhất
Iorek

5
Điều này khác với câu trả lời của João Silva, được cung cấp 7 năm trước như thế nào?
jww

7
x.translate(None, string.digits)

sẽ xóa tất cả các chữ số từ chuỗi. Để xóa các chữ cái và giữ các chữ số, hãy làm điều này:

x.translate(None, string.letters)

3
Tôi nhận được một TypeError: translate () mất chính xác một đối số (2 đã cho). Tại sao câu hỏi này được nêu lên trong trạng thái hiện tại của nó là khá bực bội.
Bobort

dịch đã thay đổi từ python 2 thành 3. Cú pháp sử dụng phương thức này trong python 3 là x.translate (str.maketrans ('', '', string.digits)) và x.translate (str.maketrans ('', '' , chuỗi.ascii_letters)). Cả hai dải trắng này. Tôi thực sự sẽ không đề xuất phương pháp này nữa ...
ZaxR

5

Các op đề cập trong các ý kiến ​​rằng ông muốn giữ vị trí thập phân. Điều này có thể được thực hiện với phương thức re.sub (theo câu trả lời tốt nhất thứ hai và IMHO) bằng cách liệt kê rõ ràng các ký tự để giữ, ví dụ:

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

Còn "poo123.4and.5fish" thì sao?
Jan Tojnar

Trong mã của tôi, tôi kiểm tra số lượng thời gian trong chuỗi đầu vào và phát sinh lỗi nếu đó là hơn 1.
Roger Heathcote

4

Phiên bản nhanh cho 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)

Đây là một so sánh hiệu suất so với 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

Vì vậy, nó nhanh hơn một chút so với regex một chút, đối với tôi. Nó cũng nhanh hơn class Delở trên, bởi vì defaultdicttất cả các tra cứu của nó trong C, thay vì (chậm) Python. Đây là phiên bản trên cùng một hệ thống của tôi, để so sánh.

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

3

Sử dụng biểu thức trình tạo:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")

thay vì làm''.join(n for n in foo if n.isdigit())
shxfee

2

Xấu xí nhưng hoạt động:

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

tại sao bạn làm list(s)vậy
SilentGhost

@SilentGhost là hiểu lầm của tôi. đã sửa chữa cảm ơn :)
Gant

Trên thực tế, với phương pháp này, tôi không nghĩ bạn cần sử dụng "tham gia". filter(lambda x: x.isdigit(), s)làm việc tốt cho tôi ... Ồ, đó là vì tôi đang sử dụng Python 2.7.
Bobort

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

100000 vòng, tốt nhất là 3: 2,48 usec mỗi vòng

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

100000 vòng, tốt nhất là 3: 2,02 usec mỗi vòng

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

100000 vòng, tốt nhất là 3: 2,37 usec mỗi vòng

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

100000 vòng, tốt nhất là 3: 1,97 usec mỗi vòng

Tôi đã quan sát thấy rằng tham gia là nhanh hơn phụ.


Tại sao bạn lặp lại hai phương pháp hai lần? Và bạn có thể mô tả câu trả lời của bạn khác với câu trả lời như thế nào không?
Jan Tojnar

Cả hai kết quả cùng một đầu ra. Nhưng, tôi chỉ muốn cho thấy rằng tham gia nhanh hơn phương thức phụ trong kết quả.
AnilReddy 17/07/18

Họ không, mã của bạn làm ngược lại. Và bạn cũng có bốn phép đo nhưng chỉ có hai phương pháp.
Jan Tojnar

1

Bạn có thể đọc từng nhân vật. Nếu nó là chữ số, sau đó đưa nó vào câu trả lời. Các str.isdigit() phương pháp là một cách để biết nếu một nhân vật là chữ số.

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

Làm thế nào khác với câu trả lời của f0b0s? Thay vào đó, bạn nên chỉnh sửa câu trả lời đó nếu bạn có thêm thông tin cần mang theo
chevybow

0

Không phải là một lót nhưng rất đơn giản:

buffer = ""
some_str = "aas30dsa20"

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

print( buffer )

0

Tôi đã sử dụng này. 'letters'nên chứa tất cả các chữ cái mà bạn muốn loại bỏ:

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

Thí dụ:

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

Đầu ra: 20

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.