在Python中从字符串中去除所有非数字字符(除了“.”)。

69

我已经有一个相当不错的代码片段,但是我想知道是否有更好的方法来实现这个功能:

val = ''.join([c for c in val if c in '1234567890.'])

你会怎么做?


12
因为我是被网络搜索带到这里的,所以我想补充一点,如果可能出现负数,人们必须不要忘记在自己的代码中加上“-”符号。 - Christian
哇,这对于简单的任务来说相当不错。 - Nikhil VJ
6个回答

128
你可以使用正则表达式(使用 re 模块)来实现相同的功能。下面的示例匹配任何非十进制数字或句点字符的序列 [^\d.] 并将它们替换为空字符串。请注意,如果使用 UNICODE 标志编译该模式,则结果字符串仍可能包含 非 ASCII 数字。此外,移除“非数字”字符后的结果不一定是有效的数字。
>>> import re
>>> non_decimal = re.compile(r'[^\d.]+')
>>> non_decimal.sub('', '12.34fe4e')
'12.344'

正则表达式可能会更快! - g06lin
15
加上量词表示我支持这种写法。请注意,在这种情况下,您不需要编译模式;Python会缓存最近使用的模式。相反,只需使用re.sub(r'[^\d.]+', '', '12.34fe4e')即可。 - Ben Blank
3
Python会缓存最近使用的模式(如果我没记错的话是最近100个),但我喜欢在这里使用编译,因为你可以通过一个合理的名称引用该模式,而不必每次阅读代码时进行正则表达式的心理解码。 - Kenan Banks
代码在355.fhfg55.ty55g处中断。 - Pranav Waila
@PranavWaila 你期望那个结果是什么? - Miles
如果是十进制数,只能有一个小数点。 - Pranav Waila

20

另一种“Pythonic”的方法

filter( lambda x: x in '0123456789.', s )

但正则表达式更快。


17

一个简单的解决方案是使用正则表达式。

import re 
re.sub("[^0-9^.]", "", data)

为什么这不是答案? - eljusticiero67

15

以下是一些样例代码:

$ cat a.py
a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    ''.join([c for c in a if c in '1234567890.'])

$ cat b.py
import re

non_decimal = re.compile(r'[^\d.]+')

a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    non_decimal.sub('', a)

$ cat c.py
a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    ''.join([c for c in a if c.isdigit() or c == '.'])

$ cat d.py
a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    b = []
    for c in a:
        if c.isdigit() or c == '.': continue
        b.append(c)

    ''.join(b)

并且这是时间测试结果:


$ time python a.py
real    0m24.735s
user    0m21.049s
sys     0m0.456s

$ time python b.py
real    0m10.775s
user    0m9.817s
sys     0m0.236s

$ time python c.py
real    0m38.255s
user    0m32.718s
sys     0m0.724s

$ time python d.py
real    0m46.040s
user    0m41.515s
sys     0m0.832s

目前看起来正则表达式是最好的选择。

就我个人而言,我认为正则表达式和列表推导式一样易读。如果你只需要使用它几次,那么编译正则表达式可能会花费更多时间。选择符合你的代码和编码风格的方式。


2
你可以使用timeit模块更轻松(和准确地)执行这些微基准测试。例如:$ python -m timeit -s "import re; non_decimal = re.compile(r'[^\d.]+'); a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'" "non_decimal.sub('', a)" - Miles
这让我得到了10.7微秒。1百万次循环的10.775秒足够接近10.7微秒了。 :) - Colin Burnett
根据你的代码和编码风格来做。 - adam

3
import string
filter(lambda c: c in string.digits + '.', s)

2
如果字符集更大,使用下面的集合可能会更快。但由于现在的字符集比较小,所以这个方法比a.py稍微慢一点。
dec = set('1234567890.')

a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    ''.join(ch for ch in a if ch in dec)

至少在我的系统上,如果你在a.py中使用生成器表达式而不是列表解析,可以节省一点时间(如果你的字符串足够长则可以节省内存):

a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    ''.join(c for c in a if c in '1234567890.')

噢,这是我在这个测试字符串中找到的迄今为止最快的方法(比正则表达式快得多),如果你需要做很多次,并且愿意承受构建几个字符表的开销。

chrs = ''.join(chr(i) for i in xrange(256))
deletable = ''.join(ch for ch in chrs if ch not in '1234567890.')

a = '27893jkasnf8u2qrtq2ntkjh8934yt8.298222rwagasjkijw'
for i in xrange(1000000):
    a.translate(chrs, deletable)

在我的系统上,该操作运行时间约为1.0秒,而正则表达式 b.py 运行时间约为4.3秒。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接