从Python的字符串中剥离字母数字字符以外的所有内容


337

使用Python从字符串中剥离所有非字母数字字符的最佳方法是什么?

这个问题PHP变体中提供的解决方案可能会进行一些小的调整,但对我来说似乎并不是很“ pythonic”。

作为记录,我不仅要删除句点和逗号(和其他标点符号),而且还要删除引号,方括号等。


7
您是否关心国际字母数字字符,例如“æøå”,“مرحبا”,“สวัสดี”,“こんにちは”?
Pimin Konstantin Kefaloukos 2014年

4
@PiminKonstantinKefaloukos是的,我确实关心国际字符,因此我对使用re.UNICODE的可接受答案的评论。
Mark van Lent 2014年

Answers:


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微秒isalnum()。尽管如此,它仍然比正则表达式要慢得多。)
DrAl

+1,测量时间不错!(但倒数第二,要做的是pattern.sub('', string.printable)-当您有RE对象时愚蠢地调用re.sub!-)。
Alex Martelli

46
记录:用于re.compile('[\W_]+', re.UNICODE)使其成为Unicode安全。
Mark van Lent

3
如何在不删除空白的情况下做到这一点?
maudulus 2014年

6
在不删除空格的情况下执行此操作:re.sub('[\ W _] +','',句子,flags = re.UNICODE)
PALEN

268

救援的正则表达式:

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

通过Python的定义'\W== [^a-zA-Z0-9_],其中不包括所有的numbersletters_


2
加号在正则表达式中起什么作用?(我知道这是什么意思,只是好奇为什么需要重新购买。)
马克·范·伦特

7
@Mark:我想这会加快替换速度,因为替换操作会一次性删除所有非单词字符,而不是一个接一个地删除它们。
DrAl

2
是的,我在一段时间前调整了一些对性能至关重要的代码的同时进行了测试。如果有很大的跨度来替换字符,则提速是巨大的。
蚂蚁阿斯玛09年

20
在这种情况下,它可能无关紧要,但\W也会保留下划线。
Blixt

12
按照@Blixt提示,如果您只需要字母和数字,则可以执行re.sub(r'[^ a-zA-Z0-9]','',your_string)
Nigini 2012年

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
使用翻译确实确实要快得多。即使在进行替换/转换之前添加for循环(以减少安装成本),转换仍然比我的机器上的regexp快约17倍。很高兴知道。
Mark van Lent

3
这绝对是最pythonic的解决方案。
codygman 2012年

1
这几乎说服了我,但我建议使用string.punctuation而不是''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols 2015年

1
请注意,这适用于str对象,但不适用于unicode对象。
Yavar

@John Machin本质上是作为参数传递给的列表理解.join()吗?
AdjunctProfessorFalcon

41

您可以尝试:

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

我喜欢你的答案,但它消除了阿拉伯语字符也可以告诉我如何让他们
谢里夫DZ

13

怎么样:

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

这是通过使用列表中理解产生字符的列表中InputString,如果它们存在于合并ascii_lettersdigits字符串。然后,它将列表连接到一个字符串中。


看来string.ascii_letters仅包含字母(duh),而不包含数字。我还需要数字...
Mark van Lent,2009年

添加string.digits确实可以解决我刚才提到的问题。:)
马克范·伦特

是的,我意识到当我回头阅读您的问题时。自我注意:学习阅读!
DrAl

4

作为此处其他答案的补充,我提供了一种非常简单而灵活的方法来定义一组您希望将字符串内容限制为的字符。在这种情况下,我允许使用字母数字加破折号和下划线。只需PERMITTED_CHARS根据自己的用例从我添加或删除字符。

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

2
使用而不是硬编码容易出现细微错误的允许字符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()语句检查当前char是否为字母符号(如果是),则将其连接到sent变量,sent = "".join()并且所有非字母符号都将被替换为""(空字符串),因为的join功能。
Sysanin

因为这是每个字符执行一个循环,而不是依赖C正则表达式,这不是很慢吗?
dcsan

3
for char in my_string:
    if not char.isalnum():
        my_string = my_string.replace(char,"")

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.maketransstr.translate最快,但包含所有非ASCII字符。 re.compilepattern.sub较慢,但比''.join&更快filter


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

如果此答案有用,请评价此!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.