파이썬에서 문자열에서 영숫자 문자를 제외한 모든 것을 제거


337

파이썬을 사용하여 영숫자가 아닌 모든 문자를 문자열에서 제거하는 가장 좋은 방법은 무엇입니까?

이 질문PHP 변형에 제시된 솔루션은 약간의 조정으로 작동하지만 아마도 나에게 '피 토닉'하지는 않습니다.

기록을 위해 마침표와 쉼표 (및 기타 문장 부호)를 제거하고 따옴표, 대괄호 등을 제거하고 싶지 않습니다.


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 마이크로이었다 isalnum().. 옵션 여전히 훨씬 느린 비록 정규 표현식보다
DrAl

+1, 측정 시간이 좋습니다! (하지만 두 번째로, pattern.sub('', string.printable)대신 RE 객체가있을 때 re.sub를 호출하는 것은 바보입니다!-).
Alex Martelli

46
레코드의 경우 : re.compile('[\W_]+', re.UNICODE)유니 코드 안전을 위해 사용 하십시오.
Mark van Lent

3
공백을 제거하지 않고 어떻게합니까?
maudulus

6
공백을 제거하지 않고 수행하십시오. re.sub ( '[\ W _] +', '', 문장, flags = re.UNICODE)
PALEN

268

구조에 대한 정규식 :

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

파이썬 정의에 '\W== [^a-zA-Z0-9_], 제외 모든 numbers, letters_


2
더하기 기호는 정규 표현식에서 무엇을합니까? (나는 그것이 왜 필요한지 궁금해하는 것이 무엇인지 알고 있습니다.)
Mark van Lent

7
@ Mark : 바꾸기는 단어가 아닌 모든 문자를 하나씩 제거하는 대신 한 번에 한 블록으로 제거하므로 대체 속도를 높일 것이라고 생각합니다.
DrAl

2
예, 성능이 중요한 코드를 얼마 전에 조정하면서 벤치마킹했습니다. 대체 할 문자 범위가 많으면 속도가 크게 향상됩니다.
Ants 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
번역을 사용하는 것이 실제로 훨씬 빠릅니다. 대체 / 번역을 수행하기 전에 for 루프를 추가하더라도 (설정 비용을 줄이려면) 여전히 내 기계의 정규 표현식보다 약 17 배 빠르게 번역합니다. 알아 둘만 한.
Mark van Lent

3
이것은 확실히 가장 파이썬적인 솔루션입니다.
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

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

나는 당신의 답변을 사랑했지만 아랍어 문자도 제거합니다. 문자를 유지하는 방법을 알려주십시오
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)])

이것은 목록 이해를 사용하여 문자 InputString가 결합 ascii_lettersdigits문자열 에있는 경우 문자 목록을 생성합니다 . 그런 다음 목록을 문자열로 결합합니다.


string.ascii_letters에는 숫자가 아닌 문자 (duh) 만 포함되어있는 것 같습니다. 나도 숫자가 필요해 ...
Mark van Lent

string.digits를 추가하면 방금 언급 한 문제가 해결됩니다. :)
Mark van Lent

네, 다시 질문을 읽으 러 갔다는 것을 깨달았습니다. 자기주의 사항 : 읽는 법을 배우십시오!
DrAl

4

여기에 다른 답변에서 나온 것처럼 문자열 내용을 제한하려는 문자 집합을 정의하는 매우 간단하고 유연한 방법을 제공합니다. 이 경우 알파벳 숫자 PLUS 대시와 밑줄을 허용합니다. 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()문을 통해 확인 합니다. 그렇다면 sent변수에 연결 sent = "".join()하고 알파벳이 아닌 모든 기호는 ""(빈 문자열) 의 join기능.
Sysanin

이것이 C 정규 표현식에 의존하기보다는 문자 당 루프를 수행하기 때문에 이것이 매우 느리지 않습니까?
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는 느리지 만 ''.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)

이 답변이 도움이된다면 이것을 평가 해주세요!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.