Python에서 문자열의 이스케이프 시퀀스 처리


112

때때로 파일이나 사용자로부터 입력을 받으면 이스케이프 시퀀스가 ​​포함 된 문자열을 얻습니다. 파이썬이 문자열 리터럴에서 이스케이프 시퀀스를 처리하는 것과 같은 방식으로 이스케이프 시퀀스를 처리하고 싶습니다 .

예를 들어 myString다음과 같이 정의 한다고 가정 해 보겠습니다 .

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs

나는 이것을 수행하는 함수를 원한다 process.

>>> print(process(myString))
spam
eggs

함수가 Python의 모든 이스케이프 시퀀스를 처리 할 수 ​​있어야합니다 (위 링크의 표에 나열 됨).

파이썬에이를 수행하는 기능이 있습니까?


1
흠, 포함 된 문자열 'spam'+"eggs"+'''some'''+"""more"""이 처리 될 것으로 정확히 어떻게 예상 합니까?
Nas Banov

@Nas Banov 좋은 테스트입니다. 해당 문자열에는 이스케이프 시퀀스가 ​​포함되어 있지 않으므로 처리 후 정확히 동일해야합니다. myString = "'spam'+\"eggs\"+'''some'''+\"\"\"more\"\"\"", print(bytes(myString, "utf-8").decode("unicode_escape"))작동하는 것 같습니다.
dln385

5
이 질문에 대한 대부분의 답변에는 심각한 문제가 있습니다. 유니 코드를 깨지 않고 파이썬에서 이스케이프 시퀀스를 존중하는 표준 방법은없는 것 같습니다. @rspeer가 게시 한 답변은 지금까지 알려진 모든 사례를 처리하기 때문에 Grako에 채택한 답변 입니다.
Apalala

답변:


138

올바른 방법은 '문자열 이스케이프'코드를 사용하여 문자열을 디코딩하는 것입니다.

>>> myString = "spam\\neggs"
>>> decoded_string = bytes(myString, "utf-8").decode("unicode_escape") # python3 
>>> decoded_string = myString.decode('string_escape') # python2
>>> print(decoded_string)
spam
eggs

AST 또는 eval을 사용하지 마십시오. 문자열 코덱을 사용하는 것이 훨씬 안전합니다.


3
손을 내려, 최고의 솔루션! btw, 문서에 따르면 "string_escape"(밑줄 포함)이어야하지만 어떤 이유로 '문자열 이스케이프', '문자열 @ 이스케이프'및 기타 등등 ... 기본적으로'string\W+escape'
Nas Banov

2
@Nas Banov 문서는 그것에 대해 작은 언급을합니다 :Notice that spelling alternatives that only differ in case or use a hyphen instead of an underscore are also valid aliases; therefore, e.g. 'utf-8' is a valid alias for the 'utf_8' codec.
dln385

30
이 솔루션은 원래 문자열에 합법적 인 유니 코드 문자가있는 경우를 처리하지 않기 때문에 충분하지 않습니다. 시도하는 경우 >>> print("juancarlo\\tañez".encode('utf-8').decode('unicode_escape')) : juancarlo añez
Apalala 2014-07-01

2
@Apalala와 동의 : 이것만으로는 충분하지 않습니다. Python2 및 3에서 작동하는 완전한 솔루션은 아래 rseeper의 답변을 확인하십시오!
기독교 AICHINGER

2
에서 latin1가정 하므로 unicode_escape인코딩 / 디코딩 비트를 다시 실행합니다. 예s.encode('utf-8').decode('unicode_escape').encode('latin1').decode('utf8')
메타 토스터

121

unicode_escape 일반적으로 작동하지 않습니다

그것은 밝혀 그 string_escape또는unicode_escape 특히, 실제 유니 코드의 존재에 일을하지 않습니다 - 솔루션은 일반적으로 작동하지 않습니다.

모든 비 ASCII 문자가 이스케이프 된다는 것을 확신 할 수 있다면 (그리고 처음 128자를 초과하는 것은 비 ASCII unicode_escape라는 점을 기억하십시오) 올바른 일을 할 것입니다. 그러나 문자열에 이미 ASCII가 아닌 문자가 있으면 문제가 발생합니다.

unicode_escape기본적으로 바이트를 유니 코드 텍스트로 변환하도록 설계되었습니다. 그러나 많은 곳에서 (예 : Python 소스 코드) 소스 데이터는 이미 유니 코드 텍스트입니다.

이것이 올바르게 작동 할 수있는 유일한 방법은 먼저 텍스트를 바이트로 인코딩하는 것입니다. UTF-8은 모든 텍스트에 적합한 인코딩이므로 작동해야합니다.

다음 예제는 Python 3에 있으므로 문자열 리터럴이 더 깨끗하지만 Python 2와 3 모두에서 약간 다른 표현으로 동일한 문제가 존재합니다.

>>> s = 'naïve \\t test'
>>> print(s.encode('utf-8').decode('unicode_escape'))
naïve   test

글쎄, 그건 틀렸어.

텍스트를 텍스트로 디코딩하는 코덱을 사용하는 새로운 권장 방법은 codecs.decode직접 호출 하는 것입니다. 도움이 되나요?

>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve   test

전혀. (또한 위는 Python 2의 UnicodeError입니다.)

unicode_escape코덱은, 그 이름에도 불구하고, 모든 비 ASCII 바이트 라틴-1 (ISO-8859-1) 인코딩에 있다고 가정하는 것이 밝혀졌습니다. 따라서 다음과 같이해야합니다.

>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve    test

그러나 그것은 끔찍합니다. 이것은 마치 유니 코드가 전혀 발명되지 않은 것처럼 256 개의 Latin-1 문자로 제한됩니다!

>>> print('Ernő \\t Rubik'.encode('latin-1').decode('unicode_escape'))
UnicodeEncodeError: 'latin-1' codec can't encode character '\u0151'
in position 3: ordinal not in range(256)

문제를 해결하기 위해 정규식 추가

(놀랍게도 두 가지 문제가 없습니다.)

우리가해야 할 일은 unicode_escape우리가 ASCII 텍스트라고 확신하는 것들 에만 디코더를 적용하는 것입니다. 특히 ASCII 텍스트로 보장되는 유효한 Python 이스케이프 시퀀스에만 적용 할 수 있습니다.

계획은 정규 표현식을 사용하여 이스케이프 시퀀스를 찾고, re.sub이스케이프되지 않은 값으로 대체 하기 위해 함수를 인수로 사용하는 것입니다.

import re
import codecs

ESCAPE_SEQUENCE_RE = re.compile(r'''
    ( \\U........      # 8-digit hex escapes
    | \\u....          # 4-digit hex escapes
    | \\x..            # 2-digit hex escapes
    | \\[0-7]{1,3}     # Octal escapes
    | \\N\{[^}]+\}     # Unicode characters by name
    | \\[\\'"abfnrtv]  # Single-character escapes
    )''', re.UNICODE | re.VERBOSE)

def decode_escapes(s):
    def decode_match(match):
        return codecs.decode(match.group(0), 'unicode-escape')

    return ESCAPE_SEQUENCE_RE.sub(decode_match, s)

그리고 그것으로 :

>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő     Rubik

2
이와 같은 더 포괄적 인 유형의 답변이 필요합니다. 감사.
v.oddou

이것은 os.sep전혀 작동합니까 ? 이 작업을하려고하는데 patt = '^' + self.prefix + os.sep ; name = sub(decode_escapes(patt), '', name)작동하지 않습니다. 세미콜론이 새 줄 대신 사용됩니다.
Pureferret

@Pureferret 나는 당신이 무엇을 요구하는지 잘 모르겠지만 아마도 Windows 파일 경로와 같이 백 슬래시가 다른 의미를 갖는 문자열에서 이것을 실행해서는 안됩니다. (그것이 당신의 os.sep것입니까?) Windows 디렉토리 이름에 백 슬래시 이스케이프 시퀀스가있는 경우 상황은 거의 복구 할 수 없습니다.
rspeer

이스케이프 시퀀스에 이스케이프가 없지만 '가짜 이스케이프 문자열'오류가 발생합니다
Pureferret

백 슬래시로 다른 정규식을 끝냈다는 것을 알 수 있습니다. stackoverflow.com/questions/4427174/…
rspeer

33

파이썬 3에 대한 실제로 정확하고 편리한 대답 :

>>> import codecs
>>> myString = "spam\\neggs"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
spam
eggs
>>> myString = "naïve \\t test"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
naïve    test

에 관한 세부 사항 codecs.escape_decode:

  • codecs.escape_decode 바이트 단위 디코더입니다.
  • codecs.escape_decodeb"\\n"-> b"\n", b"\\xce"-> 와 같은 ASCII 이스케이프 시퀀스를 디코딩 b"\xce"합니다.
  • codecs.escape_decode 는 바이트 객체의 인코딩에 대해 신경 쓰지 않거나 알 필요가 없지만 이스케이프 된 바이트의 인코딩은 나머지 객체의 인코딩과 일치해야합니다.

배경:

  • @rspeer 가 정확합니다 : unicode_escapepython3에 대한 잘못된 솔루션입니다. 이는 unicode_escape이스케이프 된 바이트를 디코딩 한 다음 바이트를 유니 코드 문자열로 디코딩하지만 두 번째 작업에 사용할 코덱에 대한 정보를 수신하지 않기 때문입니다.
  • @Jerub 이 정확합니다 : AST 또는 eval을 피하십시오.
  • 내가 처음 발견 codecs.escape_decode에서 이 대답은 "내가 어떻게 .decode 않습니다 ( '문자열 탈출') Python3에?" . 그 대답에서 알 수 있듯이 해당 함수는 현재 파이썬 3에 대해 문서화되지 않았습니다.

이것이 진짜 대답입니다 (: 문서화가 잘
안된

5
이것은 당신이 가진 이스케이프 시퀀스 \x가 UTF-8 바이트 의 이스케이프 인 상황에 대한 대답입니다 . 그러나 바이트를 바이트로 디코딩하기 때문에 이스케이프와 같은 비 ASCII 유니 코드 문자의 이스케이프를 디코딩 할 수 없으며 디코딩 할 수도 없습니다 \u.
rspeer

참고로이 기능은 기술적으로 공개되지 않습니다. bugs.python.org/issue30588
Hack5

8

ast.literal_eval기능은 가깝게 있지만, 문자열이 제대로 첫번째 인용 될 것으로 예상됩니다.

물론 백 슬래시 이스케이프에 대한 Python의 해석은 문자열이 인용되는 방식 ( ""vs r""vs u"", 삼중 따옴표 등)에 따라 다르므로 사용자 입력을 적절한 따옴표로 묶고에 전달할 수 literal_eval있습니다. 따옴표로 묶으 literal_eval면 숫자, 튜플, 사전 등이 반환 되지 않습니다 .

사용자가 문자열을 감싸려는 유형의 인용되지 않은 따옴표를 입력하면 여전히 까다로울 수 있습니다.


내가 참조. 이것은 당신이 말하는 것처럼 잠재적으로 위험한 것 같습니다 : myString = "\"\ndoBadStuff()\n\"", print(ast.literal_eval('"' + myString + '"'))코드를 실행하려고 시도하는 것 같습니다. 어떻게 ast.literal_eval보다 다른 / 안전 eval?
dln385

5
@ dln385 : literal_eval코드를 실행하지 않습니다. 문서에서 "이것은 값을 직접 구문 분석 할 필요없이 신뢰할 수없는 소스의 Python 표현식이 포함 된 문자열을 안전하게 평가하는 데 사용할 수 있습니다."
Greg Hewgill

2

이것은 나쁜 방법이지만 문자열 인수로 전달 된 이스케이프 된 8 진수를 해석하려고 할 때 효과적이었습니다.

input_string = eval('b"' + sys.argv[1] + '"')

eval과 ast.literal_eval 사이에 차이가 있다는 점을 언급 할 가치가 있습니다 (eval이 훨씬 안전하지 않음). 파이썬의 eval () 대 ast.literal_eval () 사용하기를 참조하십시오 .


0

아래 코드는 \ n에 대해 작동해야 문자열에 표시되어야합니다.

import string

our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)

1
이것은 쓰여진대로 작동하지 않고 (슬래시 replace는 아무것도하지 않음) 매우 오래된 API를 사용합니다 ( string이 종류 의 모듈 함수는 Python 2.0에서 더 이상 사용되지 않고 str메서드 로 대체 되었으며 Python 3에서 완전히 사라짐). 일반적인 이스케이프 처리가 아닌 단일 개행을 대체하는 특정 경우를 처리합니다.
ShadowRanger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.