>백엔드 개발 >파이썬 튜토리얼 >Python의 문자열 및 문자 인코딩에 대한 자세한 소개

Python의 문자열 및 문자 인코딩에 대한 자세한 소개

高洛峰
高洛峰원래의
2017-03-20 10:01:041199검색

1. 서문


Python의 문자 인코딩은 일반적인 주제이며 동료들은 이와 관련하여 많은 기사를 썼습니다. 어떤 사람은 자신의 말을 따르고, 어떤 사람은 깊이 있게 글을 씁니다. 최근에 유명 훈련기관에서 이 문제에 대해 다시 이야기하는 강의 영상을 보았는데, 설명이 여전히 불만족스러워서 이 글을 쓰고 싶었습니다. 한편으로는 관련된 지식을 정리하고 싶고, 다른 한편으로는 다른 분들께 조금이나마 도움이 되었으면 좋겠습니다.

Python2의 기본 인코딩은 ASCII이므로 한자를 인식할 수 없으며 문자 인코딩을 명시적으로 지정해야 합니다. Python3의 기본 인코딩은 한자를 인식할 수 있는 유니코드입니다.

"파이썬에서의 중국어 처리"에 대해 위와 같은 설명을 많은 글에서 보신 적이 있으실 거라 생각하며, 처음 그런 설명을 봤을 때 정말 이해하셨으리라 믿습니다. 그러나 오랜 시간이 지나도 관련된 문제에 계속해서 직면하게 되면, 자신이 그것을 명확하게 이해하지 못한다는 느낌을 받게 될 것입니다. 위에서 언급한 기본 인코딩이 무엇인지 이해하면 해당 문장의 의미를 더 명확하게 이해할 수 있습니다.

"문자 인코딩이란 무엇인가"와 "문자 인코딩의 개발 과정"은 이 섹션에서 논의되는 주제가 아니라는 점에 유의해야 합니다. 이러한 내용에 대해서는 이전 < <기사>

2. 관련 개념


1. 문자와 바이트

문자는 바이트와 동일하지 않으며, 문자는 사람이 인식할 수 있다. 그리고 이러한 기호는 컴퓨터 저장 장치에 저장되기 위해 컴퓨터가 인식할 수 있는 바이트로 표시되어야 합니다. 문자에는 여러 가지 표현 방법이 있는 경우가 많으며, 표현 방법마다 다른 바이트 수를 사용합니다. 여기에 언급된 다양한 표현 방법은 문자 인코딩을 참조합니다. 예를 들어 문자 A-Z는 ASCII 코드(1바이트 점유), UNICODE(2바이트 점유) 또는 UTF-8(1바이트 점유)로 표시될 수 있습니다. 문자 인코딩의 역할은 사람이 인식할 수 있는 문자를 기계가 인식할 수 있는 바이트 코드로 변환하고 그 반대 과정을 수행하는 것입니다.

UNICDOE는 실제 문자열인 반면, ASCII, UTF-8, GBK 등과 같은 문자 인코딩은 바이트 문자열을 나타냅니다. 이와 관련하여 Python의 공식 문서에서 다음과 같은 설명을 자주 볼 수 있습니다. "유니코드 문자열", "유니코드 문자열을 바이트 시퀀스로 변환"

코드를 작성할 때 다음과 같이 작성됩니다. 파일에 문자가 저장되고 문자는 바이트 형식으로 파일에 저장되므로 파일에 문자열을 정의하면 바이트 문자열로 처리되는 것이 이해됩니다. 그러나 바이트열이 아닌 문자열이 필요합니다. 훌륭한 프로그래밍 언어는 둘 사이의 관계를 엄격하게 구분하고 영리하고 완벽한 지원을 제공해야 합니다. JAVA 언어는 너무 좋아서 Python과 PHP를 배우기 전에는 프로그래머가 처리해서는 안되는 이러한 문제를 고려한 적이 없습니다. 불행하게도 많은 프로그래밍 언어는 "문자열"과 "바이트 문자열"을 혼동하려고 합니다. PHP와 Python2는 모두 이 프로그래밍 언어에 속합니다. 이 문제를 가장 잘 보여주는 연산은 중국어 문자가 포함된 문자열의 길이를 구하는 것입니다.

  • 문자열의 길이를 구하면 결과는 모든 문자열의 개수가 되어야 합니다. 중국어 또는 영어

  • 문자열에 해당하는 바이트 문자열의 길이는 인코딩 프로세스에서 사용되는 문자 인코딩과 관련됩니다(예: UTF-8 인코딩, 한자는 3이 필요함). GBK 인코딩, 한 문자 표현에는 2바이트 필요)

참고: Windows cmd 터미널의 기본 문자 인코딩은 GBK이므로 cmd 한자에 입력되는 문자 인코딩은 2바이트로 표현해야 합니다

>>> # Python2
>>> a = 'Hello,中国'  # 字节串,长度为字节个数 = len('Hello,')+len('中国') = 6+2*2 = 10
>>> b = u'Hello,中国'  # 字符串,长度为字符个数 = len('Hello,')+len('中国') = 6+2 = 8
>>> c = unicode(a, 'gbk')  # 其实b的定义方式是c定义方式的简写,都是将一个GBK编码的字节串解码(decode)为一个Uniocde字符串
>>> 
>>> print(type(a), len(a))
(<type &#39;str&#39;>, 10)
>>> print(type(b), len(b))
(<type &#39;unicode&#39;>, 8)
>>> print(type(c), len(c))
(<type &#39;unicode&#39;>, 8)
>>>

Python 3에서 문자열 지원이 크게 변경되었습니다. 구체적인 내용은 아래에서 소개하겠습니다.

2. 인코딩 및 디코딩

먼저 대중적인 과학을 해보겠습니다. 유니코드 문자 인코딩도 문자와 숫자의 매핑이지만 여기서 숫자는 실제로 코드 포인트라고 합니다. 16진수.

공식 Python 문서에는 유니코드 문자열, 바이트 문자열 및 인코딩 간의 관계에 대한 설명이 있습니다.

유니코드 문자열은 코드 포인트의 시퀀스이며 코드 포인트 값 범위는 0입니다. 0x10FFFF(해당 십진수 값은 1114111)입니다. 이 코드 포인트 시퀀스는 스토리지(메모리 및 물리적 디스크 포함)에서 바이트 집합(0에서 255 사이의 값)으로 표현되어야 하며, 유니코드 문자열을 바이트 시퀀스로 변환하는 규칙을 인코딩이라고 합니다.

这里说的编码不是指字符编码,而是指编码的过程以及这个过程中所使用到的Unicode字符的代码点与字节的映射规则。这个映射不必是简单的一对一映射,因此编码过程也不必处理每个可能的Unicode字符,例如:

将Unicode字符串转换为ASCII编码的规则很简单--对于每个代码点:

  • 如果代码点数值<128,则每个字节与代码点的值相同

  • 如果代码点数值>=128,则Unicode字符串无法在此编码中进行表示(这种情况下,Python会引发一个UnicodeEncodeError异常)

将Unicode字符串转换为UTF-8编码使用以下规则:

  • 如果代码点数值<128,则由相应的字节值表示(与Unicode转ASCII字节一样)

  • 如果代码点数值>=128,则将其转换为一个2个字节,3个字节或4个字节的序列,该序列中的每个字节都在128到255之间。

简单总结:

  • 编码(encode):将Unicode字符串(中的代码点)转换特定字符编码对应的字节串的过程和规则

  • 解码(decode):将特定字符编码的字节串转换为对应的Unicode字符串(中的代码点)的过程和规则

可见,无论是编码还是解码,都需要一个重要因素,就是特定的字符编码。因为一个字符用不同的字符编码进行编码后的字节值以及字节个数大部分情况下是不同的,反之亦然。

三、Python中的默认编码


1. Python源代码文件的执行过程

我们都知道,磁盘上的文件都是以二进制格式存放的,其中文本文件都是以某种特定编码的字节形式存放的。对于程序源代码文件的字符编码是由编辑器指定的,比如我们使用Pycharm来编写Python程序时会指定工程编码和文件编码为UTF-8,那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。当执行Python代码文件中的代码时,Python解释器在读取Python代码文件中的字节串之后,需要将其转换为UNICODE字符串(decode过程)之后才执行后续操作。

上面已经解释过,这个转换过程(decode,解码)需要我们指定文件中保存的字节使用的字符编码是什么,才能知道这些字节在UNICODE这张万国码和统一码中找到其对应的代码点是什么。这里指定字符编码的方式大家都很熟悉,如下所示:

# -*- coding:utf-8 -*-

 详解Python中的字符串与字符编码介绍

2. 默认编码

那么,如果我们没有在代码文件开始的部分指定字符编码,Python解释器就会使用哪种字符编码把从代码文件中读取到的字节转换为UNICODE代码点呢?就像我们配置某些软件时,有很多默认选项一样,需要在Python解释器内部设置默认的字符编码来解决这个问题,这就是文章开头所说的“默认编码”。因此大家所说的Python中文字符问题就可以总结为一句话:当无法通过默认的字符编码对字节进行转换时,就会出现解码错误(UnicodeEncodeError)。

Python2和Python3的解释器使用的默认编码是不一样的,我们可以通过sys.getdefaultencoding()来获取默认编码:

>>> # Python2
>>> import sys
>>> sys.getdefaultencoding()
'ascii'

>>> # Python3
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

因此,对于Python2来讲,Python解释器在读取到中文字符的字节码尝试解码操作时,会先查看当前代码文件头部是否有指明当前代码文件中保存的字节码对应的字符编码是什么。如果没有指定则使用默认字符编码"ASCII"进行解码导致解码失败,导致如下错误:

SyntaxError: Non-ASCII character '\xc4' in file xxx.py on line 11, but no encoding declared; 
see http://python.org/dev/peps/pep-0263/ for details

对于Python3来讲,执行过程是一样的,只是Python3的解释器以"UTF-8"作为默认编码,但是这并不表示可以完全兼容中文问题。比如我们在Windows上进行开发时,Python工程及代码文件都使用的是默认的GBK编码,也就是说Python代码文件是被转换成GBK格式的字节码保存到磁盘中的。Python3的解释器执行该代码文件时,试图用UTF-8进行解码操作时,同样会解码失败,导致如下错误:

SyntaxError: Non-UTF-8 code starting with '\xc4' in file xxx.py on line 11, but no encoding declared; 
see http://python.org/dev/peps/pep-0263/ for details

3. 最佳实践

  • 创建一个工程之后先确认该工程的字符编码是否已经设置为UTF-8

  • 为了兼容Python2和Python3,在代码头部声明字符编码:-*- coding:utf-8 -*-

四、Python2与Python3中对字符串的支持


其实Python3中对字符串支持的改进,不仅仅是更改了默认编码,而是重新进行了字符串的实现,而且它已经实现了对UNICODE的内置支持,从这方面来讲Python已经和JAVA一样优秀。下面我们来看下Python2与Python3中对字符串的支持有什么区别:

Python2

Python2中对字符串的支持由以下三个类提供

class basestring(object)
    class str(basestring)
    class unicode(basestring)

执行help(str)和help(bytes)会发现结果都是str类的定义,这也说明Python2中str就是字节串,而后来的unicode对象对应才是真正的字符串。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

a = '你好'
b = u'你好'

print(type(a), len(a))
print(type(b), len(b))

输出结果:

(<type &#39;str&#39;>, 6)
(<type &#39;unicode&#39;>, 2)

Python3

Python3中对字符串的支持进行了实现类层次的上简化,去掉了unicode类,添加了一个bytes类。从表面上来看,可以认为Python3中的str和unicode合二为一了。

class bytes(object)
class str(object)

实际上,Python3中已经意识到之前的错误,开始明确的区分字符串与字节。因此Python3中的str已经是真正的字符串,而字节是用单独的bytes类来表示。也就是说,Python3默认定义的就是字符串,实现了对UNICODE的内置支持,减轻了程序员对字符串处理的负担。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

a = '你好'
b = u'你好'
c = '你好'.encode('gbk')

print(type(a), len(a))
print(type(b), len(b))
print(type(c), len(c))

输出结果:

<class &#39;str&#39;> 2
<class &#39;str&#39;> 2
<class &#39;bytes&#39;> 4

五、字符编码转换


上面提到,UNICODE字符串可以与任意字符编码的字节进行相互转换,如图:

 详解Python中的字符串与字符编码介绍

那么大家很容易想到一个问题,就是不同的字符编码的字节可以通过Unicode相互转换吗?答案是肯定的。

Python2中的字符串进行字符编码转换过程是:

字节串-->decode('原来的字符编码')-->Unicode字符串-->encode('新的字符编码')-->字节串

#!/usr/bin/env python
# -*- coding:utf-8 -*-


utf_8_a = '我爱中国'
gbk_a = utf_8_a.decode('utf-8').encode('gbk')
print(gbk_a.decode('gbk'))

输出结果:

我爱中国
Python3中定义的字符串默认就是unicode,因此不需要先解码,可以直接编码成新的字符编码:

字符串-->encode('新的字符编码')-->字节串

#!/usr/bin/env python
# -*- coding:utf-8 -*-


utf_8_a = '我爱中国'
gbk_a = utf_8_a.encode('gbk')
print(gbk_a.decode('gbk'))

输出结果:

我爱中国

最后需要说明的是,Unicode不是有道词典,也不是google翻译器,它并不能把一个中文翻译成一个英文。正确的字符编码的转换过程只是把同一个字符的字节表现形式改变了,而字符本身的符号是不应该发生变化的,因此并不是所有的字符编码之间的转换都是有意义的。怎么理解这句话呢?比如GBK编码的“中国”转成UTF-8字符编码后,仅仅是由4个字节变成了6个字节来表示,但其字符表现形式还应该是“中国”,而不应该变成“你好”或者“China”。

 

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

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