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.