다양한 답변의 비트와 조각 덕분에 우리는 설명을 할 수 있다고 생각합니다.
파이썬은 유니 코드 문자열 u '\ xe9'를 인쇄하려고하여 현재 sys.stdout.encoding에 저장된 인코딩 체계를 사용하여 해당 문자열을 암시 적으로 인코딩하려고합니다. 파이썬은 실제로 시작된 환경에서이 설정을 선택합니다. 환경에서 적절한 인코딩을 찾을 수없는 경우에만 기본 ASCII (ASCII)로 되돌 립니다.
예를 들어 인코딩 기본값은 UTF-8 인 bash 셸을 사용합니다. 파이썬을 시작하면 그 설정을 받아 사용합니다.
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
잠시 파이썬 셸을 종료하고 가짜 인코딩으로 bash 환경을 설정합시다.
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
그런 다음 파이썬 쉘을 다시 시작하고 실제로 기본 ASCII 인코딩으로 되 돌리는 지 확인하십시오.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
빙고!
이제 ASCII 외부에서 유니 코드 문자를 출력하려고하면 멋진 오류 메시지가 표시됩니다
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
파이썬을 끝내고 bash 쉘을 버릴 수 있습니다.
이제 파이썬이 문자열을 출력 한 후 어떤 일이 발생하는지 살펴 보겠습니다. 이를 위해 먼저 그래픽 터미널 내에서 bash 쉘을 시작하고 (Gnome Terminal을 사용합니다) ISO-8859-1 aka latin-1 (그래픽 터미널은 일반적으로 문자 설정 옵션이 있습니다) 드롭 다운 메뉴 중 하나에서 인코딩 ). 이것은 실제 쉘 환경의 인코딩을 변경하지 않으며 , 터미널 자체가 웹 브라우저와 약간 다르게 주어진 출력을 디코딩 하는 방식 만 변경합니다 . 따라서 쉘 환경에서 터미널 인코딩을 독립적으로 변경할 수 있습니다. 그런 다음 셸에서 Python을 시작하고 sys.stdout.encoding이 셸 환경의 인코딩 (UTF-8)으로 설정되어 있는지 확인하십시오.
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>
(1) 파이썬은 그대로 이진 문자열을 출력하고, 터미널은 그것을 받아 라틴 -1 문자 맵과 그 값을 일치 시키려고 시도합니다. latin-1에서 0xe9 또는 233은 문자 "é"를 생성하므로 터미널이 표시됩니다.
(2) python 은 현재 sys.stdout.encoding에 설정된 체계를 사용하여 유니 코드 문자열 을 암시 적으로 인코딩 하려고 시도합니다 .이 경우 "UTF-8"입니다. UTF-8 인코딩 후 결과 이진 문자열은 '\ xc3 \ xa9'입니다 (나중에 설명 참조). 터미널은 이와 같이 스트림을 수신하고 latin-1을 사용하여 0xc3a9를 디코딩하려고 시도하지만 latin-1은 0에서 255로 이동하므로 스트림은 한 번에 1 바이트 만 디코딩합니다. 0xc3a9는 2 바이트 길이이므로 latin-1 디코더는이를 0xc3 (195) 및 0xa9 (169)로 해석하여 Ã 및 ©의 두 문자를 생성합니다.
(3) 파이썬은 라틴 코드 체계로 유니 코드 코드 포인트 u '\ xe9'(233)를 인코딩합니다. 라틴 -1 코드 포인트 범위는 0-255이며 해당 범위 내에서 유니 코드와 정확히 동일한 문자를 가리 킵니다. 따라서 해당 범위의 유니 코드 코드 포인트는 latin-1로 인코딩 될 때 동일한 값을 생성합니다. 따라서 라틴 -1로 인코딩 된 u '\ xe9'(233)는 이진 문자열 '\ xe9'도 생성합니다. 터미널은이 값을 받아서 latin-1 문자 맵에서 일치 시키려고합니다. 사례 (1)과 마찬가지로 "é"가 표시되고 이것이 표시됩니다.
드롭 다운 메뉴에서 터미널의 인코딩 설정을 UTF-8로 변경해 보겠습니다 (웹 브라우저의 인코딩 설정을 변경하는 것처럼). Python을 중지하거나 셸을 다시 시작할 필요가 없습니다. 터미널의 인코딩은 이제 파이썬의 인코딩과 일치합니다. 다시 인쇄 해 봅시다 :
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) 파이썬은 그대로 이진 문자열을 출력합니다 . 터미널은 UTF-8을 사용하여 해당 스트림을 디코딩하려고 시도합니다. 그러나 UTF-8은 0xe9 값을 이해하지 못하므로 (나중에 설명 참조) 유니 코드 코드 포인트로 변환 할 수 없습니다. 코드 포인트가없고 문자가 인쇄되지 않습니다.
(5) 파이썬 은 sys.stdout.encoding에있는 것으로 유니 코드 문자열 을 암시 적으로 인코딩 하려고 시도합니다 . 여전히 "UTF-8"입니다. 결과 이진 문자열은 '\ xc3 \ xa9'입니다. 터미널은 스트림을 수신하고 UTF-8을 사용하여 0xc3a9를 디코딩하려고 시도합니다. 유니 코드 문자 맵에서 "é"기호를 가리키는 코드 값 0xe9 (233)를 반환합니다. 터미널에 "é"가 표시됩니다.
(6) 파이썬은 라틴 -1로 유니 코드 문자열을 인코딩하면 동일한 값 '\ xe9'의 이진 문자열을 생성합니다. 다시 말하지만, 터미널의 경우는 케이스 (4)와 거의 동일합니다.
결론 :-파이썬은 기본 인코딩을 고려하지 않고 비 유니 코드 문자열을 원시 데이터로 출력합니다. 터미널은 현재 인코딩이 데이터와 일치하는 경우 이들을 표시합니다. -Python은 sys.stdout.encoding에 지정된 체계를 사용하여 인코딩 한 후 유니 코드 문자열을 출력합니다. -파이썬은 셸 환경에서 해당 설정을 가져옵니다. -터미널은 자체 인코딩 설정에 따라 출력을 표시합니다. -터미널의 인코딩은 쉘의 인코딩과 독립적입니다.
유니 코드, UTF-8 및 라틴 -1에 대한 자세한 내용 :
유니 코드는 기본적으로 일부 키 (코드 포인트)가 일부 심볼을 가리 키도록 할당 된 문자 표입니다. 예를 들어, 일반적으로 키 0xe9 (233)는 'é'기호를 가리키는 값으로 결정되었습니다. ASCII와 유니 코드는 0에서 127까지의 동일한 코드 포인트를 사용합니다 (라틴 -1 및 유니 코드는 0에서 255까지). 즉, 0x41은 ASCII의 경우 A를 가리키고, 라틴 -1 및 유니 코드는 0xc8은 'Ü'을 가리 킵니다. latin-1 및 Unicode, 0xe9는 latin-1 및 Unicode에서 'é'를 가리 킵니다.
전자 장치로 작업 할 때 유니 코드 코드 포인트는 전자적으로 표현할 수있는 효율적인 방법이 필요합니다. 그것이 인코딩 체계에 관한 것입니다. 다양한 유니 코드 인코딩 체계 (utf7, UTF-8, UTF-16, UTF-32)가 있습니다. 가장 직관적이고 직접적인 인코딩 방식은 유니 코드 맵의 코드 포인트 값을 전자 형식의 값으로 사용하는 것이지만 현재 유니 코드에는 백만 개 이상의 코드 포인트가 있으므로 일부는 3 바이트가 필요합니다. 표현했다. 텍스트를 효율적으로 사용하려면 1 대 1 매핑은 실제 요구 사항에 관계없이 모든 코드 포인트를 문자 당 최소 3 바이트로 정확히 동일한 공간에 저장해야하기 때문에 다소 비실용적입니다.
대부분의 인코딩 체계에는 공간 요구 사항에 대한 단점이 있으며, 가장 경제적 인 것은 모든 유니 코드 코드 포인트를 다루지는 않습니다. 예를 들어 ascii는 첫 128 개만 다루고 라틴 -1은 첫 256 개만 다루고 있습니다. 일반적인 "저렴한"문자라도 필요한 것보다 많은 바이트가 필요하기 때문에 낭비입니다. 예를 들어 UTF-16은 ASCII 범위의 문자를 포함하여 문자 당 최소 2 바이트를 사용합니다 ( 'B'는 65이지만 여전히 UTF-16에서 2 바이트의 저장 공간이 필요합니다). UTF-32는 모든 문자를 4 바이트로 저장하므로 훨씬 더 낭비입니다.
UTF-8은 다양한 양의 바이트 공간으로 코드 포인트를 저장할 수있는 체계로 딜레마를 영리하게 해결했습니다. 인코딩 전략의 일환으로 UTF-8은 공간 요구 사항과 경계를 나타내는 플래그 비트로 코드 포인트를 묶습니다 (아마도 디코더에).
ASCII 범위 (0-127)에서 유니 코드 코드 포인트의 UTF-8 인코딩 :
0xxx xxxx (in binary)
- x는 인코딩하는 동안 코드 포인트를 "저장"하도록 예약 된 실제 공간을 보여줍니다.
- 선행 0은 UTF-8 디코더에이 코드 포인트에 1 바이트 만 필요함을 나타내는 플래그입니다.
- 인코딩시 UTF-8은 해당 특정 범위의 코드 포인트 값을 변경하지 않습니다 (예 : UTF-8로 인코딩 된 65도 65). 유니 코드와 ASCII도 같은 범위에서 호환된다는 점을 고려하면 우연히 UTF-8과 ASCII도 해당 범위에서 호환됩니다.
예를 들어, 'B'에 대한 유니 코드 코드 포인트는 바이너리에서 '0x42'또는 0100 0010입니다 (우리가 말했듯이 ASCII와 동일합니다). UTF-8로 인코딩 한 후에는 다음과 같이됩니다.
0xxx xxxx <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010 <-- Unicode code point 0x42
0100 0010 <-- UTF-8 encoded (exactly the same)
127보다 높은 유니 코드 코드 포인트의 UTF-8 인코딩 (비 ASCII) :
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- 선행 비트 '110'은 UTF-8 디코더에 2 바이트로 인코딩 된 코드 포인트의 시작을 나타내고 '1110'은 3 바이트를 나타내고 11110은 4 바이트 등을 나타냅니다.
- 내부 '10'플래그 비트는 내부 바이트의 시작을 알리는 데 사용됩니다.
- 다시, x는 인코딩 후 유니 코드 코드 포인트 값이 저장되는 공간을 표시합니다.
예를 들어 'é'유니 코드 코드 포인트는 0xe9 (233)입니다.
1110 1001 <-- 0xe9
UTF-8이이 값을 인코딩하면 값이 127보다 크고 2048보다 작은 것으로 결정되므로 2 바이트로 인코딩해야합니다.
110x xxxx 10xx xxxx <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001 <-- 0xe9
1100 0011 1010 1001 <-- 'é' after UTF-8 encoding
C 3 A 9
UTF-8 인코딩 후 0xe9 유니 코드 코드 포인트는 0xc3a9가됩니다. 터미널이 정확히받는 방법입니다. 터미널이 라틴 -1 (비 유니 코드 레거시 인코딩 중 하나)을 사용하여 문자열을 디코딩하도록 설정된 경우 라틴 -1의 0xc3이 Ã 및 0xa9에서 ©를 가리 키기 때문에 Ã ©가 표시됩니다.