资讯专栏INFORMATION COLUMN

关于Python编码这一篇文章就够了

ispring / 1262人阅读

摘要:每个字符都可以编码为唯一的序列。但在中还有其他的数字系统,通过其他方式是表示数字。事实上,是的完美子集。里的编码与解码的类型用于表示人类可读的文本,可以包含任何字符。编码的文本表示为二进制数据字节。

概述

在使用Python或者其他的编程语言,都会多多少少遇到编码错误,处理起来非常痛苦。在Stack Overflow和其他的编程问答网站上,UnicodeDecodeError和UnicodeEncodeError也经常被提及。本篇教程希望能帮你认识Python编码,并能够从容的处理编码问题。

本教程提到的编码知识并不限定在Python,其他语言也大同小异,但我们依然会以Python为主,来演示和讲解编码知识。

通过该教程,你将学习到如下的知识:

获取有关字符编码和数字系统的概念

理解编码如何使用Python的str和bytes

通过int函数了解Python对数字系统的支持

熟悉Python字符编码和数字系统相关的内置函数

什么是字符编码

现在的编码规则已经有好多了,最简单、最基本是的ASCII编码,只要是你学过计算机相关的课程,你就应该多少了解一点ASCII编码,他是最小也是最适合了解字符编码原理的编码规则。具体如下:

小写英文字符:a-z

大写英文字符:A-Z

符号: 比如 $和!

空白符:回车、换行、空格等

一些不可打印的字符: 比如b等

那么,字符编码的定义到底是什么了?它是一种将字符(如字母,标点符号,符号,空格和控制字符)转换为整数并最终转换为bit进行存储的方法。 每个字符都可以编码为唯一的bit序列。 如果你对bit的概念不了解,请不要担心,我们后面会介绍。

ASCII码的字符被分为如下几组:

ASCII表一共包括128个字符,如果你想了解整个ASCII表,这里有

Python string模块

string模块是python里处理字符串很方便的模块,它包括了整个ASCII字符,让我们来看看部分string模块源码:

# From lib/python3.7/string.py

whitespace = " 	

vf"
ascii_lowercase = "abcdefghijklmnopqrstuvwxyz"
ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ascii_letters = ascii_lowercase + ascii_uppercase
digits = "0123456789"
hexdigits = digits + "abcdef" + "ABCDEF"
octdigits = "01234567"
punctuation = r"""!"#$%&"()*+,-./:;<=>?@[]^_`{|}~"""
printable = digits + ascii_letters + punctuation + whitespace

你可以在Python中这样使用string模块:

>>> import string

>>> s = "What"s wrong with ASCII?!?!?"
>>> s.rstrip(string.punctuation)
"What"s wrong with ASCII"
什么是bit

学过计算机相关课程的同学,应该都知道,bit是计算机内部存储单位,只有0和1两个状态(二进制),我们上面所说的ASCII表,都是一个10进制的数字表示一个字符,而这个10进制数字,最终会转换成0和1,存储在计算机内部。例如(第一列是10进制数字,第二列是二进制,第三列是计算机内部存储结果):

这是一种在Python中将ASCII字符串表示为位序列的方便方法。 ASCII字符串中的每个字符都被伪编码为8位,8位序列之间有空格,每个字符代表一个字符:

>>> def make_bitseq(s: str) -> str:
...     if not s.isascii():
...         raise ValueError("ASCII only allowed")
...     return " ".join(f"{ord(i):08b}" for i in s)

>>> make_bitseq("bits")
"01100010 01101001 01110100 01110011"

>>> make_bitseq("CAPS")
"01000011 01000001 01010000 01010011"

>>> make_bitseq("$25.43")
"00100100 00110010 00110101 00101110 00110100 00110011"

>>> make_bitseq("~5")
"01111110 00110101"

我们也可以是用python的f-string 来格式化,比如f"{ord(i):08b}":

冒号的左侧是ord(i),它是实际的对象,其值将被格式化并插入到输出中。 使用ord()为单个str字符提供了base-10代码点。

冒号的右侧是格式说明符。 08表示宽度为8,0填充,b用作在基数2(二进制)中输出结果数的符号。

ASCII编码不够用了

ASCII采用的是8bit来存储字符(只使用7位,剩下的1位二进制为0),所以,ASCII最多存储128个字符,这有个简单的公式,计算存储字符的bit数量与存储字符总数的关系:2的n次方,n表示bit数量。例如:

1bit存储2个字符

8bit存储256个字符

64bit存储2的64次方 == 18,446,744,073,709,551,616

我们可以写个简单的代码,来计算一下,指定字符数量,至少需要多少bit来存储:

>>> from math import ceil, log

>>> def n_bits_required(nvalues: int) -> int:
...     return ceil(log(nvalues) / log(2))

>>> n_bits_required(256)
8
数字系统

在上面的ASCII讨论中,您看到每个字符映射到0到127范围内的整数。但在CPython中还有其他的数字系统,通过其他方式是表示数字。除了十进制外,python还支持以下几个方式:

Binary: 2进制

Octal: 8进制

Hexadecimal (hex): 16进制

你可能要问,为什么有了十进制,还要支持这么多其他进制的数字了?这个取决你的业务场景和操作系统,在Python里,把str转换成int,默认是10进制的。

>>> int("11")
11
>>> int("11", base=10)  # 10 is already default
11
>>> int("11", base=2)  # Binary
3
>>> int("11", base=8)  # Octal
9
>>> int("11", base=16)  # Hex
17

你可以在赋值时,直接告诉解释器数字的类型,不同进制标表示方法如下:

类型 前缀 示例
n/a n/a 11
二进制 0b 或者 0B 0b11
八进制 0o 或者 0O 0o11
十六进制 0x 或者 0X 0x11
>>> 11
11
>>> 0b11  # 二进制
3
>>> 0o11  # 八进制
9
>>> 0x11  # 16进制
17
深入Unicode

正如您所看到的,ASCII的问题在于它不是一个足够大的字符集来容纳世界上的语言,方言,符号和字形。 (这对于英语来说甚至都不够大。)Unicode从根本上起到与ASCII相同的作用,但是Unicode拥有更大的存储空间,具有1,114,112个可能的字符,能够完全包含世界上所有的语言。事实上,ASCII是Unicode的完美子集。 Unicode表中的前128个字符与您合理期望的ASCII字符完全对应。

Unicode本身不是编码,但是有很多遵循Unicode编码规范编码,后面讲到的UTF-8就是其中一个。

Unicode vs UTF-8

Unicode是一种抽象编码标准,而不是编码。这就是UTF-8和其他编码方案发挥作用的地方。 Unicode标准(字符到代码点的映射)从其单个字符集定义了几种不同的编码。UTF-8及其较少使用的表兄弟UTF-16和UTF-32是用于将Unicode字符表示为每个字符一个或多个字节的二进制数据的编码格式。我们稍后将讨论UTF-16和UTF-32,但到目前为止,UTF-8占据了最大份额。

Python 3里的编码与解码

Python 3的str类型用于表示人类可读的文本,可以包含任何Unicode字符。

相反,字节类型表示二进制数据或原始字节序列,它们本质上没有附加编码。

编码和解码是从一个到另一个的过程:

decode 和 encode 函数,默认编码是utf-8:

>>> "résumé".encode("utf-8")
b"rxc3xa9sumxc3xa9"
>>> "El Niño".encode("utf-8")
b"El Nixc3xb1o"

>>> b"rxc3xa9sumxc3xa9".decode("utf-8")
"résumé"
>>> b"El Nixc3xb1o".decode("utf-8")
"El Niño"

str.encode()的结果是一个bytes对象,bytes对象只允许ASCII字符。这就是为什么在调用“ElNiño”.encode(“utf-8”)时,允许ASCII兼容的“El”按原样表示,但带有波浪号的n被转义为“ xc3 xb1”。 这个看起来很乱的序列代表两个字节,十六进制为0xc3和0xb1:

>>> " ".join(f"{i:08b}" for i in (0xc3, 0xb1))
"11000011 10110001"
Python3一切字符皆Unicode

默认情况下,Python 3源代码假定为UTF-8。 这意味着您不需要# - - 编码:UTF-8 - - 位于Python 3中.py文件的顶部。

默认情况下,所有文本(str)都是Unicode。 编码的Unicode文本表示为二进制数据(字节)。 str类型可以包含任何文字Unicode字符,例如“Δv/Δt”,所有这些字符都将存储为Unicode。

Unicode字符集中的任何内容都是标识符中的犹太符号,这意味着résumé=“〜/ Documents / resume.pdf”是有效的,虽然这看起来很花哨。

Python的re模块默认为re.UNICODE标志而不是re.ASCII。 这意味着,例如,r“ w”匹配Unicode字符,而不仅仅是ASCII字母。

str.encode()和bytes.decode()中的默认编码是UTF-8。

还有一个更细微的属性,即内置的open()的默认编码是依赖于平台的,并且取决于locale.getpreferredencoding()的值:

>>> # Mac OS X High Sierra
>>> import locale
>>> locale.getpreferredencoding()
"UTF-8"

>>> # Windows Server 2012; other Windows builds may use UTF-16
>>> import locale
>>> locale.getpreferredencoding()
"cp1252"

一个关键特性是UTF-8是一种可变长度编码。回想一下关于ASCII的部分。 扩展ASCII-land中的所有内容最多需要一个字节的空间。 您可以使用以下生成器表达式快速证明这一点:

>>> all(len(chr(i).encode("ascii")) == 1 for i in range(128))
True

UTF-8完全不同。 给定的Unicode字符可以占用1到4个字节。 以下是占用四个字节的单个Unicode字符的示例:

>>> ibrow = "           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43897.html

相关文章

  • 关于Python编码这一文章够了

    摘要:每个字符都可以编码为唯一的序列。但在中还有其他的数字系统,通过其他方式是表示数字。事实上,是的完美子集。里的编码与解码的类型用于表示人类可读的文本,可以包含任何字符。编码的文本表示为二进制数据字节。 概述 在使用Python或者其他的编程语言,都会多多少少遇到编码错误,处理起来非常痛苦。在Stack Overflow和其他的编程问答网站上,UnicodeDecodeError和Unic...

    YorkChen 评论0 收藏0
  • Python字符串的格式化,看这一够了

    摘要:相信很多人在格式化字符串的时候都用的语法,提出一种更先进的格式化方法并成为的标准用来替换旧的格式化语法,从开始已经实现了这一方法其它解释器未考证。 showImg(https://segmentfault.com/img/remote/1460000018650325); 相信很多人在格式化字符串的时候都用%s % v的语法,PEP 3101 提出一种更先进的格式化方法 str.for...

    BDEEFE 评论0 收藏0
  • Python 与 Unicode

    摘要:最近使用处理一些网络相关的问题,被相关的一系列编码问题搞得一头雾水。与接下来是中对于字符串的处理。中的和在中,其类型规定了底层的数据结构,是位整数串,也即跟语言中的字符串类似。这些问题在中得到解决。 最近使用 Python 2 处理一些网络相关的问题,被 Unicode, String 相关的一系列编码问题搞得一头雾水。在这里整理一下相关的概念吧。 ASCII Unicode UTF8...

    Tangpj 评论0 收藏0
  • 使用 Python 进行并发编程系列 - 收藏集 - 掘金

    摘要:使用进行并发编程篇三掘金这是使用进行并发编程系列的最后一篇。所以我考虑启用一个本地使用进行并发编程篇二掘金我们今天继续深入学习。 使用 Python 进行并发编程 - asyncio 篇 (三) - 掘金 这是「使用Python进行并发编程」系列的最后一篇。我特意地把它安排在了16年最后一天。 重新实验上篇的效率对比的实现 在第一篇我们曾经对比并发执行的效率,但是请求的是httpb...

    MorePainMoreGain 评论0 收藏0
  • Python 3 入门,看这篇够了

    摘要:缩进不一致,会导致运行错误。变量变量在使用前必须先定义即赋予变量一个值,否则会报错数据类型布尔只有和两个值,表示真或假。 简介 Python 是一种高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。Python 由 Guido van Rossum 于 1989 年底在荷兰国家数学和计算机科学研究所发明,第一个公开发行版发行于 1991 年。 特点 易于学习:Python ...

    Shimmer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<