이러한 인코딩 문제에 대한 모든 열쇠는 원칙적으로이 있다는 것을 이해하는 것입니다 "문자열"의 두 가지 개념 (1)의 문자열 : 문자 의, (2) 문자열 / 배열 바이트. 이 구분은 256 자 이하의 인코딩 (ASCII, Latin-1, Windows-1252, Mac OS Roman 등)의 역사적 편재성 때문에 오랫동안 대부분 무시되었습니다. 이러한 인코딩은 공통 문자 집합을 다음에 매핑합니다. 0에서 255 사이의 숫자 (예 : 바이트); 웹이 등장하기 전에 상대적으로 제한된 파일 교환으로 인해 호환되지 않는 인코딩 상황이 허용됩니다. 대부분의 프로그램은 동일한 운영 체제에 남아있는 텍스트를 생성하는 한 여러 인코딩이 있다는 사실을 무시할 수 있습니다. 이러한 프로그램은 단순히 운영 체제에서 사용하는 인코딩을 통해 텍스트를 바이트로 처리합니다. 정확하고 현대적인보기는 다음 두 가지 사항을 기반으로이 두 문자열 개념을 적절하게 분리합니다.
문자 는 대부분 컴퓨터와 관련이 없습니다 . 예를 들어 بايثون, 中 蟒 및 🐍과 같이 초크 보드 등에 그릴 수 있습니다. 기계의 "문자"에는 공백, 캐리지 리턴, 쓰기 방향 설정 지침 (아랍어 등), 악센트 등과 같은 "그리기 지침"도 포함됩니다. 유니 코드 표준 에는 매우 큰 문자 목록 이 포함되어 있습니다. 알려진 문자의 대부분을 다룹니다.
반면에 컴퓨터는 어떤 방식 으로든 추상 문자를 표현해야합니다.이를 위해 메모리가 바이트 청크로 제공되기 때문에 바이트 배열 (0에서 255 사이의 숫자 포함)을 사용합니다. 문자를 바이트로 변환하는 데 필요한 프로세스를 인코딩 이라고 합니다. 따라서 컴퓨터 는 문자를 표현하기 위해 인코딩이 필요 합니다. 컴퓨터에있는 모든 텍스트는 표시 될 때까지 인코딩되거나 (특정 방식으로 인코딩 된 문자를 예상하는) 터미널로 전송되거나 파일에 저장됩니다. 표시되거나 적절하게 "이해"(예 : Python 인터프리터)하기 위해 바이트 스트림이 문자로 디코딩 됩니다. 몇 가지 인코딩(UTF-8, UTF-16,…)은 문자 목록에 대해 유니 코드에 의해 정의됩니다. 따라서 유니 코드는 문자 목록과 이러한 문자에 대한 인코딩을 모두 정의합니다. "유니 코드 인코딩"이라는 표현을 유비쿼터스 UTF-8을 참조하는 방법이지만 유니 코드는 여러 인코딩을 제공하므로 잘못된 용어 입니다.
요약하면 컴퓨터는 내부적으로 bytes 를 사용하여 문자를 나타내야 하며 다음 두 가지 작업을 통해 수행합니다.
인코딩 : 문자 → 바이트
디코딩 : 바이트 → 문자
일부 인코딩은 모든 문자 (예 : ASCII)를 인코딩 할 수 없지만 (일부) 유니 코드 인코딩을 사용하면 모든 유니 코드 문자를 인코딩 할 수 있습니다. 일부 문자는 직접 또는 조합 (예 : 기본 문자 및 악센트) 으로 표현 될 수 있기 때문에 인코딩이 반드시 고유 한 것은 아닙니다 .
개행 이라는 개념은 운영 체제에 따라 다른 (제어) 문자로 표현 될 수 있기 때문에 복잡한 계층을 추가 합니다 (이것이 Python의 범용 개행 파일 읽기 모드 의 이유입니다 ).
이제 위에서 "문자"라고 부르는 것은 유니 코드가 " 사용자 인식 문자 " 라고 부르는 것 입니다. 사용자가 인식하는 단일 문자는 " 코드 포인트 " 라고하는 유니 코드 목록의 서로 다른 색인 에있는 문자 부분 (기본 문자, 악센트 등) 을 결합하여 유니 코드로 표현할 수 있습니다. 이러한 코드 포인트를 함께 결합하여 형성 할 수 있습니다. "문자 소 클러스터". 따라서 유니 코드는 바이트와 문자열 사이에 있고 후자에 더 가까운 유니 코드 코드 포인트 시퀀스로 구성된 세 번째 문자열 개념으로 이어집니다. 나는 그것들을 " 유니 코드 문자열 " 이라고 부를 것이다 (파이썬 2 에서처럼).
Python은 (사용자가 인식하는) 문자의 문자열을 인쇄 할 수 있지만 Python이 아닌 바이트 문자열은 기본적 으로 사용자가 인식하는 문자가 아니라 유니 코드 코드 포인트의 시퀀스입니다 . 코드 포인트 값은 Python \u
및 \U
유니 코드 문자열 구문 에서 사용되는 값 입니다. 문자 인코딩과 혼동해서는 안됩니다 (그리고 그와 어떤 관계도 가질 필요가 없습니다 : 유니 코드 코드 포인트는 다양한 방법으로 인코딩 될 수 있습니다).
이것은 중요한 결과를 가져 옵니다 : Python (유니 코드) 문자열의 길이는 코드 포인트의 수이며, 항상 사용자가 인식하는 문자의 수 는 아닙니다 . 따라서 s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3)은 단일 사용자가 인식 (한국어) 각 len 3
했음에도 불구하고 제공합니다. s
(그렇게 할 필요가 없더라도 3 개의 코드 포인트로 표현되기 때문입니다 print("\uac01")
.) 그러나 많은 실제 상황에서 많은 문자가 일반적으로 Python에서 단일 유니 코드 코드 포인트로 저장되기 때문에 문자열의 길이는 사용자가 인식하는 문자의 수입니다.
에서는 파이썬 2 유니 코드 문자열 "유니 코드 문자열 '(...라고 unicode
형 리터럴 형태 u"…"
바이트 배열은"문자열 "(반면) str
바이트 어레이는 예를 들어 문자열 상수로 구성 될 수있는 형식 "…"
). 에서 파이썬 3 , 유니 코드 문자열은 단순히 "문자열"(라고 str
유형, 문자 양식을 "…"
바이트 배열은 "바이트"(반면) bytes
유형, 문자 그대로의 형태 b"…"
). 결과적으로 다음과 같은 "🐍"[0]
결과가 Python 2 ( '\xf0'
, 한 바이트) 및 Python 3 ( "🐍"
, 첫 번째이자 유일한 문자) 에서 다른 결과를 제공합니다 .
이 몇 가지 핵심 사항을 통해 대부분의 인코딩 관련 질문을 이해할 수 있습니다!
일반적으로 터미널에 인쇄 u"…"
할 때 쓰레기가 발생해서는 안됩니다. Python은 터미널의 인코딩을 알고 있습니다. 실제로 터미널에서 예상하는 인코딩을 확인할 수 있습니다.
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
입력 문자를 터미널의 인코딩으로 인코딩 할 수있는 경우 Python은이를 수행하고 불평없이 해당 바이트를 터미널로 보냅니다. 그러면 터미널은 입력 바이트를 디코딩 한 후 문자를 표시하기 위해 최선을 다할 것입니다 (최악의 경우 터미널 글꼴에는 일부 문자가없고 대신 어떤 종류의 공백이 인쇄됩니다).
입력 문자를 터미널의 인코딩으로 인코딩 할 수없는 경우 터미널이 이러한 문자를 표시하도록 구성되지 않았 음을 의미합니다. 파이썬은 불평 할 것입니다 (파이썬에서는 UnicodeEncodeError
문자열이 터미널에 맞는 방식으로 인코딩 될 수 없기 때문에). 가능한 유일한 해결책은 문자를 표시 할 수있는 터미널을 사용하는 것입니다 (문자를 나타낼 수있는 인코딩을 허용하도록 터미널을 구성하거나 다른 터미널 프로그램을 사용하여). 이것은 다른 환경에서 사용할 수있는 프로그램을 배포 할 때 중요합니다. 인쇄하는 메시지는 사용자의 터미널에서 표현할 수 있어야합니다. 따라서 때때로 ASCII 문자 만 포함하는 문자열을 사용하는 것이 가장 좋습니다.
그러나 프로그램 의 출력 을 리디렉션하거나 파이프 할 때 일반적으로 수신 프로그램의 입력 인코딩이 무엇인지 알 수 없으며 위 코드는 None (Python 2.7) 또는 UTF-8 ( 파이썬 3) :
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
그러나 stdin, stdout 및 stderr의 인코딩은 필요한 경우 환경 변수를 통해 설정할 수 있습니다 PYTHONIOENCODING
.
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
터미널에 인쇄해도 예상 한 결과가 나오지 않으면 수동으로 입력 한 UTF-8 인코딩이 올바른지 확인할 수 있습니다. 예를 들어, 내가 착각\u001A
하지 않았다면 첫 번째 문자 ( )는 인쇄 할 수 없습니다 .
에서 http://wiki.python.org/moin/PrintFails , 파이썬 2.x를위한 다음과 같은 솔루션을 찾을 수 있습니다 :
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Python 3 의 경우 이전 에 StackOverflow 에서 질문 한 질문 중 하나를 확인할 수 있습니다 .