python2与python3字符串的区别

python2

在 py2 中,有两种类型字符串:str 和 unicode。但严格的来讲,str 并非彻底意义上的字符串,把它称做 字节码串 更合适。unicode 则做为真正意义上的 字符串,但定义时须要使用 u"" 去声明。有人可能会有疑问,字符串 在内存单元中也是以字节进行存储的,怎么能将他俩区别对待呢?java

字符串 是一种 数据结构。数据结构 = 原数据 + 数据描述(为了更容易理解),而字符串的 “数据描述” 即为什么种字符集。python

字节码 则是没有数据描述的。数组

py2 中,当咱们读取 str 类型时,它只表明了一串数据,至于这串数据是何种编码,系统是不知道的,须要咱们手动指定。而 unicode 类型,则存储的相应的字符集,系统在读取时便能根据此数据描述对数据进行识别和处理。数据结构

'\xc4\xe3\xba\xc3' 对计算机来讲,就是 1 个字节的字节码串在一块儿。编码

u'\u4f60\u597d' 对计算机来讲,则是每 2 个字节做为一个单位,去映射 unicode 字符集作运算。spa

当咱们定义 str字符串 时,python 会根据咱们当前的运行上下文:源文件 / cli 去设定 str 的字符集。好比咱们在脚本开始处注释的 coding: utf-8 会将脚本中的 str 编码为 utf-8 的字节码串儿。操作系统

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

import sys

"""
做用是注明脚本上下文的字符集
好比当咱们定义一个 str 类型的数据时
它是 utf-8 字符集的字节码
"""
str = "你好" #以 utf-8 字节码去编码

"""
但不要和 python 的默认字符集搞混
默认字符集会在一些隐式加解码时被运用
"""
# ascii
print sys.getdefaultencoding()

# 设置 python 的默认字符集
reload(sys)
sys.setdefaultencoding("utf-8")

"""
下面的代码存在一次隐式的 decode
在隐式或没有指定 decode 的字符集时
会使用 默认字符集(这里已将默认字符集设为 utf-8)进行解码
若是咱们没有修改默认字符集而使用 ascii 的话
就会报错了 ascii 是没办法编码中文的
"""
"你好".encode("gb2312")


"""
py2中 write 写的为字节码串
当前源码编码字符集为 utf-8
因此写入时 "你好" 是以 utf-8 编码的
"""
with open("encode.txt", "w") as f:
    f.write("你好")

"""
读取的 str 也为 utf-8 的字节码串
"""
with open("encode.txt", "r") as f:
    str = f.read()

例如个人 cmd 是 gb2312 字符集,因此 "你好" 的字节码串以下(gb2312 一个中文字符用 2 个字节标识)。下面咱们将其转换为 utf-8 字符集的字节码:code

encode/decode 下面详细讲解。内存

python3

py3 则对字符串和字节码进行了更为规范的定义:str 和 bytes。str 终于成为了真正意义上的字符串,bytes 也形象的表征了字节码串儿(java 中的字节数组)utf-8

py3 默认支持 unicode 字符集,故不须要鸡肋的使用 py2 中的 u"xxx" 去定义一个 unicode 字符串。

简单来讲,py2 想定义一个平常的字符串,须要手动 u"xxx","xxx" 定义的是当前编码设定下的字节码串儿,典型的本末倒置。在 py3 中,再也不有 u"xxx" 的语法,"xxx" 定义的就是字符串(unicode编码),b"xxx" 定义的才是字节码串儿。

"""
py3 中消除了py2中字节码和字符串混乱的定义
str 就是字符串 bytes 才是字节码
在 py2 中 str 是字节码 unicode 才是字符串
"""
        py3        py2
字节码  bytes      str
字符串  str       unicode

因此在 py3 中,你的 coding: utf-8 的注释没有任何用处了。在 py2 中它会隐式的根据指定编码对 str 作字节码的定义,但在 py3 中已经没有这种隐式的转换和定义了。

import sys

# 默认字符集由 ascii 改成 utf-8
print("默认字符集为:%s" % (sys.getdefaultencoding()))

str = "你好"

# 2 个字符长度 py2 中就不会把 str 看为数据结构 而是字节码 因此结果会是 6
print(len(str))


# 不指定字符集将使用默认的 utf-8 进行编码
str.encode() # b'\xe4\xbd\xa0\xe5\xa5\xbd'
str.encode("utf-8") # b'\xe4\xbd\xa0\xe5\xa5\xbd'
str.encode("gb2312") # b'\xc4\xe3\xba\xc3'

hello_utf8   = b'\xe4\xbd\xa0\xe5\xa5\xbd'
hello_gb2312 = b'\xc4\xe3\xba\xc3'

# utf-8 转 gb2312 / gb2312 转 utf-8
# 都是以 unicode 做为基准进行操做
hello_utf8.decode("utf-8").encode("gb2312")
hello_gb2312.decode("gb2312").encode("utf-8")

 

encode/decode

py 以 unicode 做为字符运算的基准。

bytes  --  decode --  unicode --  encode --  bytes

unicode 编码 至其余字符集(utf-8/gb2312),其余字符集 解码 至 unicode

在 py2 中常常遇到的错误:

"你好".encode("utf-8")

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: ordinal not in range(128)

错误的缘由是什么呢?py2 以 ascii 做为默认字符集,"你好" 直接 encode 至其余编码时会进行一次隐式的解码。由于 py 以 unicode 做为运算基准,因此会先 decode 至 unicode,你没使用 py 就隐式执行,使用默认字符集 ascii 作解码,天然没法解码中文字符的字节码。

而在 py3 中是不容许这种操做的。从 bytes 到另外一 bytes 你必须先 decode 后才能 encode。

open/read/write

py2 中,open 方法并不会设定以何种字符集去打开文件,由于 py 要的不是字符串,而是字节码串儿。是的,read 和 write 操做的都是字节码串儿,字节码串儿自己并不具有表征字符的能力,咱们须要手动的指定正确的字符集进行解码。

# write.py
# -*- coding: utf-8 -*-
with open("hello.txt", "w") as f:
    f.write("你好") # 这里实际上是字节码

# read.py
# -*- coding: gbk -*-
# 结果为 6 仍然是以 utf-8 的字节码去计算的
with open("hello.txt", "r") as f:
    str = f.read()
    print len(str) # 读取的也是字节码

数据会以相应的字节码去保存

py3 中,由于存储时传入的是 unicode 字符串(而再也不是 py2 的字节码),但数据存储确定是以字节码的形式(物理存储)。因此 py3 确定会将字符串转为字节码(这个过程是py3本身完成的,咱们此时但是指定以何种字符集去编码),这时就涉及采用何种编码去进行隐式的 encode 存储和 decode 读取了。

中文系统,使用 python xxx.py 时上下文的编码为操做系统的编码:gbk。这里挺饶的,虽然我是在 gbk2312 的 cmd 里运行的,但并不是是交互模式,因此输入时并不是使用的 cmd 的字符集,而是 python 解释器启动时读取的本地系统的字符集:gbk。

#-*-coding: utf-8 -*-
"""
py3 的默认字符集为 utf-8 因此平常开发中咱们是能够省略 encoding 的
"""
with open("hello.txt", "w", encoding = "utf-8") as f:
    f.write("你好")

with open("hello.txt", "r", encoding = "utf-8") as f:
    print(f.read())
    
# -*-coding: utf-8 -*- 就算我声明了此行注释
# py3 依然不会使用其做为 open 打开文件时默认的 encoding
# 也不会使用 py3 自身默认的 sys.getdefaultencoding
# 这里 open 的 encoding 默认会与你的本地系统相关
# 因此下面我须要使用 gbk 返回字节码,从新使用 utf-8 正确解码
with open("hello.txt", "r") as f:
    print(f.read().encode("gbk").decode('utf-8')) #

# 以 b(二进制) 方式读取时为读取的字节码串儿
# 因此数据并不会受编码影响
# 咱们使用时须要指定正确的解码字符集
with open("hello.txt", "rb") as f:
    bytes = f.read()
    print(bytes)
    print(len(bytes)) # 6 utf-8 的字节码 一个中文3个字节
    print(bytes.decode("utf-8")) #正常解码 得到 "你好" unicode 字符串
    # bytes.decode("gb2312") #错误解码

因此,coding: utf-8 貌似已经没什么用处了

相关文章
相关标签/搜索