파이썬의 문자열에서 인쇄 할 수없는 문자 제거


91

나는 달리는 데 사용

$s =~ s/[^[:print:]]//g;

Perl에서 인쇄 할 수없는 문자를 제거합니다.

파이썬에는 POSIX 정규식 클래스가 없으며 내가 원하는 것을 의미하는 [: print :]를 작성할 수 없습니다. 나는 파이썬에서 문자가 인쇄 가능한지 아닌지를 감지하는 방법을 모른다.

당신은 무엇을 하시겠습니까?

편집 : 유니 코드 문자도 지원해야합니다. string.printable 방법은 출력에서 ​​행복하게 제거합니다. curses.ascii.isprint는 모든 유니 코드 문자에 대해 false를 반환합니다.

답변:


85

불행히도 파이썬에서는 문자열을 반복하는 것이 다소 느립니다. 정규 표현식은 이런 종류의 일보다 훨씬 더 빠릅니다. 캐릭터 클래스를 직접 구축하면됩니다. 한편 UnicodeData 모듈은 특히이에 매우 도움이된다 unicodedata.category () 함수입니다. 범주에 대한 설명은 유니 코드 문자 데이터베이스 를 참조하십시오 .

import unicodedata, re, itertools, sys

all_chars = (chr(i) for i in range(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(chr, itertools.chain(range(0x00,0x20), range(0x7f,0xa0))))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

Python2의 경우

import unicodedata, re, sys

all_chars = (unichr(i) for i in xrange(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(unichr, range(0x00,0x20) + range(0x7f,0xa0)))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

일부 사용 사례의 경우 추가 범주 (예 : 제어 그룹의 모든 범주 가 더 바람직 할 수 있지만 처리 시간이 느려지고 메모리 사용량이 크게 증가 할 수 있습니다. 범주 당 문자 수 :

  • Cc (대조군) : 65
  • Cf (형식) : 161
  • Cs (대리) : 2048
  • Co (개인용) : 137468
  • Cn (할당되지 않음) : 836601

편집 댓글에서 제안을 추가합니다.


4
여기에 'Cc'가 충분합니까? 모르겠습니다. 그냥 묻고 있습니다. 다른 'C'카테고리도이 필터의 후보가 될 수있는 것 같습니다.
Patrick Johnmeyer

1
이 함수는 게시 된대로 히브리어 문자의 절반을 제거합니다. 주어진 두 방법 모두에 대해 동일한 효과를 얻습니다.
dotancohen

1
성능 측면에서이 경우 string.translate ()가 더 빨리 작동하지 않습니까? stackoverflow.com/questions/265960/…
Kashyap

3
all_chars = (unichr(i) for i in xrange(sys.maxunicode))좁은 빌드 오류를 방지하기 위해 사용 합니다.
danmichaelo 2015

4
나를 위해 control_chars == '\x00-\x1f\x7f-\x9f'(Python 3.5.2에서 테스트 됨)
AXO

73

내가 아는 한 가장 비단뱀적이고 효율적인 방법은 다음과 같습니다.

import string

filtered_string = filter(lambda x: x in string.printable, myStr)

10
당신은 아마 filtered_string 원하는 = ''.join (필터 (람다 X : X는 문자열을 다시 얻을 수 있도록), string.printable myStr 인치
나단 Shively - 샌더스

12
슬프게도 string.printable에는 유니 코드 문자가 포함되어 있지 않으므로 ü 또는 ó는 출력에 포함되지 않습니다 ... 어쩌면 다른 것이 있습니까?
Vinko Vrsalovic

17
필터 + 람다가 아닌 목록 이해 또는 생성기 표현식을 사용해야합니다. 이 중 하나는 시간의 99.9 %가 더 빠를 것입니다. ''.join (s for s in myStr if s in string.printable)
habnabit

3
@AaronGallagher : 99.9 % 더 빠르나요? 어디에서 그 그림을 뽑습니까? 성능 비교는 그다지 나쁘지 않습니다.
Chris Morgan

4
안녕 윌리엄. 이 방법은 모든 비 ASCII 문자를 제거하는 것 같습니다. 유니 코드에는 인쇄 가능한 비 ASCII 문자가 많이 있습니다!
dotancohen

17

다음 unicodedata.category()기능을 사용하여 필터를 설정할 수 있습니다.

import unicodedata
printable = {'Lu', 'Ll'}
def filter_non_printable(str):
  return ''.join(c for c in str if unicodedata.category(c) in printable)

사용 가능한 범주 는 유니 코드 데이터베이스 문자 속성 에서 175 페이지의 표 4-9를 참조하십시오.


마지막 줄에서 끝나지 않는 목록 이해를 시작했습니다. 여는 브래킷을 완전히 제거하는 것이 좋습니다.
tzot

지적 해주셔서 감사합니다. 나는 그에 따라 게시물을 편집했습니다
Ber

1
이것은 가장 직접적이고 직접적인 방법 인 것 같습니다. 감사.
dotancohen

1
@CsabaToth 세 가지 모두 유효하며 동일한 집합을 산출합니다. 당신은 아마도 집합 리터럴을 지정하는 가장 좋은 방법 일 것입니다.
Ber

1
@AnubhavJhalani 필터에 더 많은 유니 코드 범주를 추가 할 수 있습니다. 문자 외에 공백과 숫자를 예약하려면printable = {'Lu', 'Ll', Zs', 'Nd'}
Ber

11

Python 3에서

def filter_nonprintable(text):
    import itertools
    # Use characters of control category
    nonprintable = itertools.chain(range(0x00,0x20),range(0x7f,0xa0))
    # Use translate to remove all non-printable characters
    return text.translate({character:None for character in nonprintable})

.translate ()가 정규식 및 .replace ()와 어떻게 비교되는지에 대해서는 구두점 제거에 대한 이 StackOverflow 게시물을 참조하십시오.

범위는 @Ants Aasma에 표시된대로 유니 코드 문자 데이터베이스 범주nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc')사용하여 생성 할 수 있습니다 .


유니 코드 범위를 사용하는 것이 더 낫습니다 (@Ants Aasma의 답변 참조). 결과는입니다 text.translate({c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0))}).
darkdragon

9

다음은 유니 코드 입력으로 작동하며 다소 빠릅니다.

import sys

# build a table mapping all non-printable characters to None
NOPRINT_TRANS_TABLE = {
    i: None for i in range(0, sys.maxunicode + 1) if not chr(i).isprintable()
}

def make_printable(s):
    """Replace non-printable characters in a string."""

    # the translate method on str removes characters
    # that map to None from the string
    return s.translate(NOPRINT_TRANS_TABLE)


assert make_printable('Café') == 'Café'
assert make_printable('\x00\x11Hello') == 'Hello'
assert make_printable('') == ''

내 자신의 테스트에 따르면이 접근 방식은 문자열을 반복하고을 사용하여 결과를 반환하는 함수보다 빠릅니다 str.join.


이것은 유니 코드 문자로 나를 위해 작동하는 유일한 대답입니다. 테스트 케이스를 제공 해주셔서 감사합니다!
pir

1
줄 바꿈을 허용하려면 LINE_BREAK_CHARACTERS = set(["\n", "\r"])and not chr(i) in LINE_BREAK_CHARACTERS테이블을 작성할 때 추가 하십시오.
pir

5

이 함수는 목록 이해력과 str.join을 사용하므로 O (n ^ 2) 대신 선형 시간으로 실행됩니다.

from curses.ascii import isprint

def printable(input):
    return ''.join(char for char in input if isprint(char))

2
filter(isprint,input)
yingted

5

파이썬 3의 또 다른 옵션 :

re.sub(f'[^{re.escape(string.printable)}]', '', my_string)

이것은 나와 1 줄에 매우 훌륭하게 작동했습니다. 감사합니다
Chop Labalagun 19

1
어떤 이유로 이것은 Windows에서는 훌륭하게 작동하지만 Linux에서는 사용할 수 없습니다. r에 대한 f를 변경해야했지만 그게 해결책인지 확실하지 않습니다.
Chop Labalagun 19

Linux Python이 너무 오래되어 f- 문자열을 지원하지 않는 것 같습니다. r- 문자열은 매우 다르지만 r'[^' + re.escape(string.printable) + r']'. (나는 re.escape()여기에서 완전히 옳다고 생각하지 않지만 그것이 작동한다면 ...)
tripleee

2

내가 지금 생각 해낸 최고는 (위의 python-izers 덕분에)

def filter_non_printable(str):
  return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])

이것이 유니 코드 문자 / 문자열에서 작동하는 유일한 방법입니다.

더 나은 옵션이 있습니까?


1
파이썬 2.3을 사용하지 않는 한 내부 []는 중복됩니다. "return ''.join (c for c ...)"
habnabit

중복되지는 않습니다. 최종 결과는 동일하지만 의미 (및 성능 특성)가 다릅니다.
Miles

범위의 다른 쪽 끝도 보호되지 않아야합니까? : "ord (c) <= 126"
Gearoid Murphy 2011

7
그러나 인쇄 할 수없는 유니 코드 문자도 있습니다.
tripleee

2

아래는 위의 다른 것보다 성능이 더 빠릅니다. 구경하다

''.join([x if x in string.printable else '' for x in Str])

"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])
evandrix 19

2

Python에는 POSIX 정규식 클래스가 없습니다.

regex라이브러리를 사용할 때 : https://pypi.org/project/regex/

잘 관리되고 유니 코드 정규식, Posix 정규식 등을 지원합니다. 사용법 (메소드 서명)입니다 매우 파이썬의 유사합니다 re.

문서에서 :

[[:alpha:]]; [[:^alpha:]]

POSIX 문자 클래스가 지원됩니다. 이들은 일반적으로의 대체 형식으로 처리됩니다 \p{...}.

(저는 제휴가 아니라 사용자입니다.)


2

@Ber의 답변에 따라 유니 코드 문자 데이터베이스 범주에 정의 된대로 제어 문자 만 제거하는 것이 좋습니다 .

import unicodedata
def filter_non_printable(s):
    return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))

이것은 훌륭한 대답입니다!
tdc

당신은 무언가를 할 수 startswith('C')있지만 이것은 다른 어떤 솔루션보다 내 테스트에서 훨씬 덜 성능이 좋았습니다.
Big McLargeHuge

big-mclargehuge : 내 솔루션의 목표는 완전성과 단순성 / 가독성의 조합이었습니다. if unicodedata.category(c)[0] != 'C'대신 사용해 볼 수 있습니다 . 더 나은 성능을 발휘합니까? 메모리 요구 사항보다 실행 속도를 선호하는 경우 stackoverflow.com/a/93029/3779655
darkdragon

0

'공백'을 제거하려면

import re
t = """
\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>
"""
pat = re.compile(r'[\t\n]')
print(pat.sub("", t))

실제로 대괄호는 필요하지 않습니다.
tripleee

0

Ants Aasmashawnrad의 답변에서 수정되었습니다 .

nonprintable = set(map(chr, list(range(0,32)) + list(range(127,160))))
ord_dict = {ord(character):None for character in nonprintable}
def filter_nonprintable(text):
    return text.translate(ord_dict)

#use
str = "this is my string"
str = filter_nonprintable(str)
print(str)

Python 3.7.7에서 테스트 됨

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