前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >字符串,字节和字符编码

字符串,字节和字符编码

原创
作者头像
Tia
修改2020-09-23 17:34:33
9630
修改2020-09-23 17:34:33
举报
文章被收录于专栏:ChiptuneChiptuneChiptune

The note of String, byte and character encodings

Based on Learn Python3 in the hard way. By Zed A. Shaw

1.例子:

下载一个名为 languages.txt 的文本文件。(下载地址: https://learnpythonthehardway.org/python3/languages.txt,点开,右键,“另存为” txt 格式,放在你的练习文件夹,再打开。)

Code:

import sys

script, encoding, error = sys.argv

def main(language_file, encoding, errors):

line = language_file.readline()

if line:

print_line(line, encoding, errors)

return main(language_file, encoding, errors)

def print_line(line, encoding, errors):

next_lang = line.strip()

raw_bytes = next_lang.encode(encoding, errors=errors)

cooked_string = raw_bytes.decode(encoding, errors=errors)

print(raw_bytes, "<===>", cooked_string)

languages = open("languages.txt", encoding="utf-8")

main(languages, encoding, error)

这些例子用了 utf-8 、utf-16 和 big5 编码来说明这种转换,以及你可能会遇到的错误类型。这些名字在 Python 3 中被称为 “codec”(编码器),但是你要用参数“encoding”。

2.解释:

2.1. 数字逻辑(数字电路)相关:

现代计算机非常复杂,但是核心就是大量的电灯开关。计算机用电来切换开关。这些开关可以以“开”代表 1,以“关”代表 0。以前有各种各样奇怪的计算机做的不只是 1 和 0 的事情,但现在所有的计算机都是一堆 1 和 0。1 代表着运行、有电、开着、进行、存在。0 代表着结束、完成、消失、关机、没电。我们把这些 1 和 0 叫做 “比特”(bits)。

那么编码到底是什么意思?它其实就是一个关于比特序列如何表示数字的公认标准,比如人们约定 00000000 就代表数字 0,11111111 就代表数字 255,00001111 就代表数字 15。现在我们把一个“字节”(byte)称为 8 个比特(1 和 0)的序列(0 -> 255)。

2.2. ASCII && Unicode

一旦你有了字节,你就可以开始存储和显示文本了,不过要用另一种惯例来让数字映射(map)成文字。美国信息交换标准编码(即 ASCII 码)成为最流行的惯例。

可以在 Python 里面试试这个(Windows Powershell 输入 python ,然后回车):

首先,我用二进制写了数字 90,然后我基于字母 'Z' 得到了对应的数字,接着我把这个数字转化成字母 'Z' 。

>>> 0b1011010

90

>>> ord ( ' Z ' )

90

>>> chr ( 90 )

' Z '

>>>

但是,ASCII 有一个问题,它只能编码英文以及一些相似的语言,而且一个字节只能表示 256 个数字(0-255,或者 00000000-11111111)。很显然,世界上正在使用的语言远远超过 256 个字符。因此不同国家创建了针对他们自己语言的编码惯例,虽然这些都管用,但是它们只适用一种语言。这就意味着,如果你想把一本英语书的书名放在一个泰语句子中,就会比较麻烦,你就需要一个泰语编码和一个英语编码。

为了解决这个问题,一群人创建了 Unicode,也就是针对所有人类语言的“统一编码”(Universal encoding)。Unicode 提供的解决方案跟 ASCII 码表类似,但是相比之下,前者更大。你可以用 32 个比特来编码一个 Unicode 字符,这比我们能找到的所有字符可能都要多。

我们现在有了针对任何字符的编码协定,但是 32 比特是 4 个字节,这就意味着对于大多数我们想要编码的文本会浪费很多空间。我们也可以用 16 比特(2 个字节),但仍然很浪费。因此后来出现了一种很妙的惯例:用 8 个比特来编码大多数通用字符,然后当我们需要编码更多字符的时候再使用更多的数字。这意味着我们有了一种压缩(compression)编码惯例,使得用 8 个比特来编码大多数常用字符,并在需要时切换成 16 或 32 个比特这件事成为可能。

2.3. 分析结果

ex23.py 脚本其实就是把字节写在 b' ' 里面,然后把它们转换成 UTF-8 编码(或者其他你设定的编码)。左边是每一个 utf-8 字节对应的数字,右边是 utf-8 实际输出的字符。之所以这样呈现,是为了让你明白 <===> 左边是 Python 用来存储字符串的数字字节或者“原始”(raw)字节,设置 b' ' 是为了告诉 Python 这是“字节”(bytes)。这些原始字节之后被“加工”(cooked)然后显示在右边,以便让你看到你的终端呈现出来的真正的字符。

2.4. 分析之前的code

import sys

script, encoding, error = sys.argv

def main(language_file, encoding, errors):

line = language_file.readline()

if line:

print_line(line, encoding, errors)

return main(language_file, encoding, errors)

def print_line(line, encoding, errors):

next_lang = line.strip()

raw_bytes = next_lang.encode(encoding, errors=errors)

cooked_string = raw_bytes.decode(encoding, errors=errors)

print(raw_bytes, "<===>", cooked_string)

languages = open("languages.txt", encoding="utf-8")

main(languages, encoding, error)

第 1-2 行: 以通常的命令行参数开始

第 5 行: 将代码的主体部分定义为一个叫“main"的函数,这个函数会在脚本最后运行的时候被调用。

第 6 行:这个函数所做的第一件事就是从给出的 languages 文件中读取一行。

第 8 行:这是一个 if 语句,它让你在 Python 代码中做决定。你可以“测试”一个变量的真假,基于其真假,运行或者不运行这段代码。在本例中,我测试了一行中是否有内容。当 readline 函数到达文件末尾的时候,它会返回空字符串,if 这一行就是为了测试这个空字符串。只要 readline 给了我们一些东西,结果就会是 true ,后面的代码就会运行(比如缩进的 9-10 行),当结果是 false 的时候, python 就会跳过 9-10 行。

第 9 行: 然后调用了一个单独的函数打印出那一行。简化了代码,并且更容易理解。一旦我知道了 print_line 是做什么的,我就可以把我的记忆附到 print_line 这个名称下,然后忘掉细节。

第 10 行: 在这儿写了一小段非常神奇的代码。main 函数内部又调用了 main 函数。如果一个叫 main 的函数只是跳到顶部,而我在这个函数的底部调用它,它就会回到顶部然后再次运行,这样就会形成一个循环(loop)。现在看第 8 行,你会看到 if 语句避免了这个函数无限循环。

第 11 行 现在开始定义 print_line 函数,它用来编码 languages.txt 文件中的每一行内容。

第 13 行 现在终于获得了从 languages.txt 中收到的语言,并把它们编码成原始字节。“DBES” à “Decode Bytes, Encode Strings”,解码字节,编码字符串。next_lang 变量是一个字符串,因此要获得原始字节,我必须对它调用 .encode() 函数来“编码字符串”。我把我想要的编码以及如何处理错误传递给 encode() 。

第 14 行 然后做了额外一步,通过从 raw_bytes 创建一个 cooked_string 变量来逆向展示第 15 行。记住,“DBES”说的是“解码字节”,raw_bytes 是字节,所以我对它调用了 .decode() 来获取一个 python 字符串。这个字符串应该和 next_lang 变量是一样的。(encode && decode)

第 15 行 我已经定义完了所有函数,现在打开 languages.txt 文件。

第 16 行 在这个脚本的结尾只是用所有正确的参数运行了 main 函数,以保证一切正常运行,避免循环。记住这个之后会跳转到第 5 行 main 函数被定义的地方,然后在第 10 行又被调用了一次,会造成它的循环。不过第 8 行的 if 语句又会阻止它无限循环。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • The note of String, byte and character encodings
  • Based on Learn Python3 in the hard way. By Zed A. Shaw
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档