>  기사  >  백엔드 개발  >  Python 인코딩 및 유니코드

Python 인코딩 및 유니코드

巴扎黑
巴扎黑원래의
2017-04-05 16:05:451080검색

유니코드와 파이썬에 대해 설명이 많이 있을 거라 확신하지만, 이해와 사용을 용이하게 하기 위해 이에 대해 몇 가지 더 쓸 계획입니다.

바이트 스트림과 유니코드 객체

먼저 Python에서 문자열을 정의해 보겠습니다. 문자열 유형을 사용하면 실제로 바이트 문자열을 저장합니다.

아아아아

이 예에서 문자열 abc는 바이트 문자열입니다. 97., 98, 99는 ASCII 코드입니다. Python 2.x의 정의는 모든 문자열을 ASCII로 처리하는 것입니다. 불행하게도 ASCII는 라틴 문자 집합 중에서 가장 흔하지 않은 표준입니다.

ASCII는 문자 매핑에 처음 127개의 숫자를 사용합니다. windows-1252 및 UTF-8과 같은 문자 맵은 처음 127자가 동일합니다. 문자열의 각 바이트 값이 127보다 작은 경우 문자열 인코딩을 혼합하는 것이 안전합니다. 그러나 아래에서 설명하는 것처럼 이러한 가정을 하는 데에는 위험이 있습니다.

문자열에 126보다 큰 값을 가진 바이트가 있으면 문제가 발생합니다. windows-1252로 인코딩된 문자열을 살펴보겠습니다. Windows-1252의 문자 매핑은 8비트 문자 매핑이므로 총 256개의 문자가 있습니다. 처음 127은 ASCII와 동일하고 다음 127은 windows-1252에서 정의한 다른 문자입니다.

아아아아

Windows-1252는 여전히 바이트 문자열이지만 마지막 바이트의 값이 126보다 큰 것을 보셨나요? Python이 기본 ASCII 표준을 사용하여 이 바이트 스트림을 디코딩하려고 시도하면 오류가 보고됩니다. Python이 이 문자열을 디코딩할 때 어떤 일이 일어나는지 살펴보겠습니다:

[  a ][  b ][  c ] = "abc"
[ 97 ][ 98 ][ 99 ] = "abc"

​ UTF-8을 사용하여 다른 문자열을 인코딩해 보겠습니다:

A windows-1252 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 150 ] = "abc–"

익숙한 유니코드 인코딩 테이블을 선택하면 영어 대시에 해당하는 유니코드 코드 포인트가 8211(0×2013)임을 알 수 있습니다. 이 값은 ASCII 최대값인 127보다 큽니다. 1바이트보다 큰 값을 저장할 수 있습니다. 8211(0×2013)은 2바이트이기 때문에 UTF-8은 몇 가지 트릭을 사용하여 하나의 문자를 저장하는 데 3바이트가 필요하다는 것을 시스템에 알려야 합니다. Python이 126보다 큰 문자 값으로 UTF-8로 인코딩된 문자열을 인코딩하기 위해 기본 ASCII를 사용하도록 준비하는 경우를 살펴보겠습니다.

아아아아

보시다시피 Python은 항상 기본적으로 ASCII 인코딩을 사용했습니다. 4번째 문자를 처리할 때 해당 값이 126보다 큰 226이기 때문에 Python에서 오류가 발생합니다. 혼합 인코딩의 문제입니다.

바이트 스트림 디코딩

Python 유니코드를 처음 배울 때 디코딩이라는 용어가 혼란스러울 수 있습니다. 바이트 스트림을 유니코드 객체로 디코딩하고 유니코드 객체를 바이트 스트림으로 인코딩할 수 있습니다.

Python은 바이트 스트림을 유니코드 객체로 디코딩하는 방법을 알아야 합니다. 바이트 스트림을 얻으면 "decode" 메소드를 호출하여 여기서 유니코드 객체를 생성합니다.

가능한 한 빨리 바이트 스트림을 유니코드로 디코딩하는 것이 좋습니다.

아아아아

​유니코드를 바이트 스트림으로 인코딩

유니코드 객체는 인코딩에 구애받지 않는 텍스트 표현입니다. 단순히 유니코드 객체를 출력할 수는 없습니다. 출력하기 전에 바이트 문자열로 변환되어야 합니다. Python은 유니코드를 바이트 스트림으로 인코딩할 때 기본적으로 ASCII를 사용하지만 이러한 종류의 작업에 적합합니다.

아아아아

코덱 모듈 사용

코덱 모듈은 바이트 스트림을 처리할 때 큰 도움을 줄 수 있습니다. 정의된 인코딩으로 파일을 열 수 있으며 파일에서 읽은 내용은 자동으로 유니코드 개체로 변환됩니다.

​이것을 시도해 보세요:

아아아아

그것이 하는 일은 유니코드 객체를 얻고 그것을 UTF-8 인코딩으로 파일에 쓰는 것입니다. 다른 상황에서도 사용할 수 있습니다.

​이것을 시도해 보세요:

파일에서 데이터를 읽을 때 codecs.open은 UTF-8로 인코딩된 파일을 유니코드 객체로 자동 변환할 수 있는 파일 객체를 생성합니다.

이번에는 urllib 스트림을 사용하여 위의 예를 계속해 보겠습니다.

아아아아

한 줄 버전:

>>> x = "abc" + chr(150)
>>> print repr(x)
'abc\x96'
>>> u"Hello" + x
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeDecodeError: &#39;ASCII&#39; codec can&#39;t decode byte 0x96 in position 3: ordinal not in range(128)

코덱 모듈에는 매우 주의해야 합니다. 전달하는 것은 유니코드 객체여야 합니다. 그렇지 않으면 자동으로 바이트 스트림을 ASCII로 디코딩합니다.

아아아아

아야, Python은 모든 것을 다시 디코딩하기 위해 ASCII를 사용하기 시작했습니다.

​UTF-8 바이트 스트림 슬라이싱 문제

UTF-8로 인코딩된 문자열은 바이트 목록이므로 len() 및 조각화 작업이 제대로 작동하지 않습니다. 먼저 이전에 사용한 문자열을 사용합니다.

아아아아

다음으로 다음을 수행하세요.

A UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"
[0x61] [0x62] [0x63] [0xe2]  [ 0x80] [ 0x93] = "abc-"

무슨 말입니까? 4자로 보이지만 len의 결과는 6이라고 나옵니다. len은 문자 수보다는 바이트 수를 계산하기 때문입니다.

아아아아

이제 이 문자열을 분할해 보겠습니다.

아아아아

가자, 분할 결과는 마지막 문자가 아닌 마지막 바이트이다.

UTF-8을 올바르게 분할하려면 바이트 스트림을 디코딩하여 유니코드 개체를 만드는 것이 좋습니다. 그러면 안전하게 작동하고 계산할 수 있습니다.

아아아아

Python이 자동으로 인코딩/디코딩되는 경우

어떤 경우에는 Python이 ASCII를 사용하여 자동으로 인코딩/디코딩할 때 오류가 발생합니다.

  第一个案例是当它试着将Unicode和字节串合并在一起的时候。

>>> u"" + u"\u2019".encode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 0:   ordinal not in range(128)

  在合并列表的时候会发生同样的情况。Python在列表里有string和Unicode对象的时候会自动地将字节串解码为Unicode。

>>> ",".join([u"This string\u2019s unicode", u"This string\u2019s utf-8".encode("utf-8")])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 11:  ordinal not in range(128)

  或者当试着格式化一个字节串的时候:

>>> "%s\n%s" % (u"This string\u2019s unicode", u"This string\u2019s  utf-8".encode("utf-8"),)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 11: ordinal not in range(128)

  基本上当你把Unicode和字节串混在一起用的时候,就会导致出错。

  在这个例子里面,你创建一个utf-8文件,然后往里面添加一些Unicode对象的文本。就会报UnicodeDecodeError错误。

>>> buffer = []
>>> fh = open("utf-8-sample.txt")
>>> buffer.append(fh.read())
>>> fh.close()
>>> buffer.append(u"This string\u2019s unicode")
>>> print repr(buffer)
[&#39;This file\xe2\x80\x99s got utf-8 in it\n&#39;, u&#39;This string\u2019s unicode&#39;]
>>> print "\n".join(buffer)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe2 in position 9: ordinal not in range(128)

  你可以使用codecs模块把文件作为Unicode加载来解决这个问题。

>>> import codecs
>>> buffer = []
>>> fh = open("utf-8-sample.txt", "r", "utf-8")
>>> buffer.append(fh.read())
>>> fh.close()
>>> print repr(buffer)
[u&#39;This file\u2019s got utf-8 in it\n&#39;, u&#39;This string\u2019s unicode&#39;]
>>> buffer.append(u"This string\u2019s unicode")
>>> print "\n".join(buffer)
This file’s got utf-8 in it

This string’s unicode

  正如你看到的,由codecs.open 创建的流在当数据被读取的时候自动地将比特串转化为Unicode。

  最佳实践

  1.最先解码,最后编码

  2.默认使用utf-8编码

  3.使用codecs和Unicode对象来简化处理

  最先解码意味着无论何时有字节流输入,需要尽早将输入解码为Unicode。这会防止出现len( )和切分utf-8字节流发生问题。

  最后编码意味着只有在准备输入的时候才进行编码。这个输出可能是一个文件,一个数据库,一个socket等等。只有在处理完成之后才编码unicode对象。最后编码也意味着,不要让Python为你编码Unicode对象。Python将会使用ASCII编码,你的程序会崩溃。

  默认使用UTF-8编码意味着:因为UTF-8可以处理任何Unicode字符,所以你最好用它来替代windows-1252和ASCII。

  codecs模块能够让我们在处理诸如文件或socket这样的流的时候能少踩一些坑。如果没有codecs提供的这个工具,你就必须将文件内容读取为字节流,然后将这个字节流解码为Unicode对象。

  codecs模块能够让你快速的将字节流转化为Unicode对象,省去很多麻烦。

  解释UTF-8

  最后的部分是让你能入门UTF-8,如果你是个超级极客可以无视这一段。

  利用UTF-8,任何在127和255之间的字节是特别的。这些字节告诉系统这些字节是多字节序列的一部分。

Our UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

  最后3字节是一个UTF-8多字节序列。如果你把这三个字节里的第一个转化为2进制可以看到以下的结果:

11100010

  前3比特告诉系统它开始了一个3字节序列226,128,147。

  那么完整的字节序列。

11100010 10000000 10010011

  然后你运用三字节序列的下面的掩码。

1110xxxx 10xxxxxx 10xxxxxx
XXXX0010 XX000000 XX010011 Remove the X&#39;s
0010       000000   010011 Collapse the numbers
00100000 00010011          Get Unicode number 0x2013, 8211 The "–"

  这是基本的UTF-8入门,如果想知道更多的细节,可以去看UTF-8的维基页面。

위 내용은 Python 인코딩 및 유니코드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.