杰瑞科技汇

python2和python3编码

核心思想:Python 2 的“默认混乱” vs Python 3 的“显式Unicode”

  • Python 2: 将字符串默认视为字节的序列,这导致了大量的混乱,开发者需要手动处理“字节串”和“Unicode字符串”之间的转换。
  • Python 3: 将字符串默认视为 Unicode 字符的序列,这使得处理国际化文本变得简单和直观,字节串和字符串是两种完全不同的类型。

Python 2 的编码世界

在 Python 2 中,有两种主要的字符串类型:

python2和python3编码-图1
(图片来源网络,侵删)
  1. str: 字节串,它是一堆字节的序列,本身不包含任何编码信息,它的行为更像是 C 语言中的 char *
  2. unicode: Unicode 字符串,它是一个抽象的字符序列,可以表示世界上几乎所有的字符。

问题根源:默认编码是 ASCII

当你写下 s = "你好" 时,Python 2 默认会使用系统的 ASCII 编码来尝试转换这些字符,因为 "你好" 不在 ASCII 字符集内,所以会抛出 SyntaxError

解决方案:你必须在字符串前加上 u 前缀,明确声明这是一个 Unicode 字符串。

# 正确写法
s_unicode = u"你好" 
print type(s_unicode)  # <type 'unicode'>
# 错误写法 (在非 ASCII 环境下)
# s_str = "你好"  # SyntaxError: Non-ASCII character '\xe4' in file ...

strunicode 的转换

当你需要将 Unicode 字符串写入文件、网络发送或与某些 C 库交互时,必须将其编码成字节串 (str),反之,从字节串读取时,需要解码成 Unicode 字符串。

  • 编码: unicode_string.encode(encoding)
  • 解码: byte_string.decode(encoding)

示例:

python2和python3编码-图2
(图片来源网络,侵删)
# 1. 定义一个 Unicode 字符串
u_str = u"你好,世界!"
# 2. 将 Unicode 字符串编码成 UTF-8 格式的字节串
utf8_bytes = u_str.encode('utf-8')
print type(utf8_bytes)  # <type 'str'>
print repr(utf8_bytes) # '\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
# 3. 将字节串解码回 Unicode 字符串
decoded_u_str = utf8_bytes.decode('utf-8')
print type(decoded_u_str) # <type 'unicode'>
print decoded_u_str      # 你好,世界!

Python 2 的“隐式转换”陷阱

Python 2 在某些情况下会尝试在 strunicode 之间进行隐式转换,这通常发生在拼接操作时,如果一方是 str,另一方是 unicode,Python 会尝试将 str 解码成 unicode(使用默认的 ASCII 编码),这常常导致 UnicodeDecodeError

s_str = "hello"      # 这是一个 str (字节串)
u_str = u"你好"       # 这是一个 unicode
# 尝试拼接
try:
    result = s_str + u_str
except UnicodeDecodeError as e:
    print "错误:", e
    # 错误: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

正确做法:总是显式地进行编码和解码。

# 正确做法
result = s_str.decode('utf-8') + u_str
# 或者
result = s_str + u_str.encode('utf-8')

Python 3 的编码世界

Python 3 对字符串模型进行了彻底的改革,消除了 Python 2 的混乱。

  1. str: Unicode 字符串,这是处理文本的默认类型,它的行为和 Python 2 的 unicode 一样。
  2. bytes: 字节串,这是一个只包含字节的序列,和 Python 2 的 str 类似。

核心思想:str 就是文本,bytes 就是数据

在 Python 3 中,"你好" 直接就是一个 Unicode 字符串,不需要 u 前缀了。

python2和python3编码-图3
(图片来源网络,侵删)
# Python 3
s = "你好"
print(type(s))  # <class 'str'>

strbytes 的转换

Python 3 不再有隐式转换,当你需要将文本(str)和二进制数据(bytes)相互转换时,必须明确地使用 .encode().decode() 方法。

  • 编码: text_string.encode(encoding)
  • 解码: byte_string.decode(encoding)

示例:

# 1. 定义一个 str (Unicode 字符串)
text = "你好,世界!"
# 2. 将 str 编码成 bytes
#    .encode() 方法返回一个 bytes 对象
byte_data = text.encode('utf-8')
print(type(byte_data)) # <class 'bytes'>
print(repr(byte_data)) # b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
# 3. 将 bytes 解码回 str
#    .decode() 方法返回一个 str 对象
decoded_text = byte_data.decode('utf-8')
print(type(decoded_text)) # <class 'str'>
print(decoded_text)      # 你好,世界!

Python 3 的严格性

Python 3 强制你区分文本和二进制数据。open() 函数默认以文本模式('r')打开文件,返回的是 str,如果你想以二进制模式('rb')打开文件,返回的是 bytes

# 文本模式 (默认)
with open('my_file.txt', 'r', encoding='utf-8') as f:
    content = f.read()  # content 是 str
    print(type(content))
# 二进制模式
with open('my_file.txt', 'rb') as f:
    content = f.read()  # content 是 bytes
    print(type(content))

总结与对比

特性 Python 2 Python 3 说明
默认字符串类型 str (字节串) str (Unicode字符串) Python 3 的 str 相当于 Python 2 的 unicode
Unicode字符串 u"..." Python 3 中 u 前缀是可选的,推荐省略。
字节串类型 str bytes Python 3 明确区分 strbytes
文件I/O open() 返回 str,需要手动编码/解码。 open() 默认文本模式,返回 str'rb' 模式返回 bytes Python 3 的文件I/O更清晰,encoding参数是文本模式的必需项。
隐式转换 存在,容易导致 UnicodeDecodeError 不存在strbytes 不能直接拼接,强制显式转换。 Python 3 的严格性避免了大量编码错误。
编码方法 unicode_str.encode() str.encode() 功能相同,但调用对象类型不同。
解码方法 byte_str.decode() bytes.decode() 功能相同,但调用对象类型不同。
打印 print "hello" (语句) print("hello") (函数) 虽然不是编码问题,但这是最基础的语法区别。

最佳实践(如何写出健壮的代码)

Python 2 兼容代码

  • 永远使用 u 前缀:在处理任何非 ASCII 文本时,都使用 u"..." 来定义 Unicode 字符串。
  • 在文件I/O时明确编码:始终在打开文件时指定编码,通常是 'utf-8'
    # Python 2
    with open('file.txt', 'r') as f:
        # 假设文件是 UTF-8 编码
        content = f.read().decode('utf-8')
  • 统一内部编码:将应用程序的内部编码统一为 Unicode,只在需要与外部世界(文件、网络、控制台)交互时,才进行编码和解码。
  • 使用 from __future__ import unicode_literals:这会使 Python 2 将所有不带 u 前缀的字符串字面量都当作 Unicode 字符串处理,减少迁移到 Python 3 的工作量。

Python 3 代码

  • 拥抱 str:放心地使用 来表示所有文本。

  • 显式处理二进制数据:当你处理文件、网络数据或图像时,明确使用 bytes 类型。

  • 始终指定文件编码

    # Python 3
    with open('file.txt', 'r', encoding='utf-8') as f:
        content = f.read()  # content 直接是 str
  • 不要混合 strbytes:如果需要拼接,先统一类型。

    # Python 3
    text = "hello"
    binary_data = b" world"
    # 错误: TypeError: can only concatenate str (not "bytes") to str
    # result = text + binary_data
    # 正确
    result = text + binary_data.decode('utf-8')
    # 或者
    result = text.encode('utf-8') + binary_data

Python 3 的编码模型是 Python 2 的巨大进步,它通过将“文本”(str)和“二进制数据”(bytes)明确分离,并强制开发者进行显式的编码和解码,从根本上解决了 Python 2 中令人头疼的 Unicode 问题,虽然对于从 Python 2 迁移过来的开发者来说需要改变一些习惯,但一旦适应,代码会变得更加健壮和易于维护。

分享:
扫描分享到社交APP
上一篇
下一篇