阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

字符串和编码

585次阅读
没有评论

共计 5045 个字符,预计需要花费 13 分钟才能阅读完成。

字符编码

我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。

因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用 8 个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是 255(二进制 11111111= 十进制 255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4 个字节可以表示的最大整数是4294967295

由于计算机是美国人发明的,因此,最早只有 127 个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为 ASCII 编码,比如大写字母 A 的编码是 65,小写字母z 的编码是122

但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和 ASCII 编码冲突,所以,中国制定了 GB2312 编码,用来把中文编进去。

你可以想得到的是,全世界有上百种语言,日本把日文编到 Shift_JIS 里,韩国把韩文编到 Euc-kr 里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

字符串和编码

因此,Unicode 字符集应运而生。Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

Unicode 标准也在不断发展,但最常用的是 UCS-16 编码,用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要 4 个字节)。现代操作系统和大多数编程语言都直接支持 Unicode。

现在,捋一捋 ASCII 编码和 Unicode 编码的区别:ASCII 编码是 1 个字节,而 Unicode 编码通常是 2 个字节。

字母 A 用 ASCII 编码是十进制的65,二进制的01000001

字符 0 用 ASCII 编码是十进制的 48,二进制的00110000,注意字符'0' 和整数 0 是不同的;

汉字 已经超出了 ASCII 编码的范围,用 Unicode 编码是十进制的20013,二进制的01001110 00101101

你可以猜测,如果把 ASCII 编码的 A 用 Unicode 编码,只需要在前面补 0 就可以,因此,A的 Unicode 编码是00000000 01000001

新的问题又出现了:如果统一成 Unicode 编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用 Unicode 编码比 ASCII 编码需要多一倍的存储空间,在存储和传输上就十分不划算。

所以,本着节约的精神,又出现了把 Unicode 编码转化为“可变长编码”的 UTF-8 编码。UTF- 8 编码把一个 Unicode 字符根据不同的数字大小编码成 1 - 6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成 4 - 6 个字节。如果你要传输的文本包含大量英文字符,用 UTF- 8 编码就能节省空间:

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
01001110 00101101 11100100 10111000 10101101

从上面的表格还可以发现,UTF- 8 编码有一个额外的好处,就是 ASCII 编码实际上可以被看成是 UTF- 8 编码的一部分,所以,大量只支持 ASCII 编码的历史遗留软件可以在 UTF- 8 编码下继续工作。

搞清楚了 ASCII、Unicode 和 UTF- 8 的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式:

在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为 UTF- 8 编码。

用记事本编辑的时候,从文件读取的 UTF- 8 字符被转换为 Unicode 字符到内存里,编辑完成后,保存的时候再把 Unicode 转换为 UTF- 8 保存到文件:

字符串和编码

浏览网页的时候,服务器会把动态生成的 Unicode 内容转换为 UTF- 8 再传输到浏览器:

字符串和编码

所以你看到很多网页的源码上会有类似 <meta charset="UTF-8" /> 的信息,表示该网页正是用的 UTF- 8 编码。

Python 的字符串

搞清楚了令人头疼的字符编码问题后,我们再来研究 Python 的字符串。

在最新的 Python 3 版本中,字符串是以 Unicode 编码的,也就是说,Python 的字符串支持多语言,例如:

>>> print('包含中文的 str')
包含中文的 str

对于单个字符的编码,Python 提供了 ord() 函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'

如果知道字符的整数编码,还可以用十六进制这么写str

>>> '\u4e2d\u6587'
'中文'

两种写法完全是等价的。

由于 Python 的字符串类型是 str,在内存中以 Unicode 表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str 变为以字节为单位的bytes

Python 对 bytes 类型的数据用带 b 前缀的单引号或双引号表示:

x = b'ABC'

要注意区分 'ABC'b'ABC',前者是 str,后者虽然内容显示得和前者一样,但bytes 的每个字符都只占用一个字节。

以 Unicode 表示的 str 通过 encode() 方法可以编码为指定的bytes,例如:

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

纯英文的 str 可以用 ASCII 编码为 bytes,内容是一样的,含有中文的str 可以用 UTF-8 编码为 bytes。含有中文的str 无法用 ASCII 编码,因为中文编码的范围超过了 ASCII 编码的范围,Python 会报错。

bytes 中,无法显示为 ASCII 字符的字节,用 \x## 显示。

反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是 bytes。要把bytes 变为 str,就需要用decode() 方法:

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

如果 bytes 中包含无法解码的字节,decode()方法会报错:

>>> b'\xe4\xb8\xad\xff'.decode('utf-8')
Traceback (most recent call last):
  ...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte

如果 bytes 中只有一小部分无效的字节,可以传入 errors='ignore' 忽略错误的字节:

>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

要计算 str 包含多少个字符,可以用 len() 函数:

>>> len('ABC')
3
>>> len('中文')
2

len()函数计算的是 str 的字符数,如果换成 byteslen() 函数就计算字节数:

>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

可见,1 个中文字符经过 UTF- 8 编码后通常会占用 3 个字节,而 1 个英文字符只占用 1 个字节。

在操作字符串时,我们经常遇到 strbytes的互相转换。为了避免乱码问题,应当始终坚持使用 UTF- 8 编码对 strbytes进行转换。

由于 Python 源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为 UTF- 8 编码。当 Python 解释器读取源代码时,为了让它按 UTF- 8 编码读取,我们通常在文件开头写上这两行:

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

第一行注释是为了告诉 Linux/OS X 系统,这是一个 Python 可执行程序,Windows 系统会忽略这个注释;

第二行注释是为了告诉 Python 解释器,按照 UTF- 8 编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了 UTF- 8 编码并不意味着你的 .py 文件就是 UTF- 8 编码的,必须并且要确保文本编辑器正在使用 UTF- 8 编码。

如果 .py 文件本身使用 UTF- 8 编码,并且也申明了# -*- coding: utf-8 -*-,打开命令提示符测试就可以正常显示中文:

字符串和编码

格式化

最后一个常见的问题是如何输出格式化的字符串。我们经常会输出类似 '亲爱的 xxx 你好!你 xx 月的话费是 xx,余额是 xx' 之类的字符串,而 xxx 的内容都是根据变量变化的,所以,需要一种简便的格式化字符串的方式。

字符串和编码

在 Python 中,采用的格式化方式和 C 语言是一致的,用 % 实现,举例如下:

>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

你可能猜到了,%运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个 %? 占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。

常见的占位符有:

占位符 替换内容
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

其中,格式化整数和浮点数还可以指定是否补 0 和整数与小数的位数:

print('%2d-%02d' % (3, 1))
print('%.2f' % 3.1415926)

如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:

>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'

有些时候,字符串里面的 % 是一个普通字符怎么办?这个时候就需要转义,用 %% 来表示一个%

>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

format()

另一种格式化字符串的方法是使用字符串的 format() 方法,它会用传入的参数依次替换字符串内的占位符{0}{1}……,不过这种方式写起来比 % 要麻烦得多:

>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'

f-string

最后一种格式化字符串的方法是使用以 f 开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:

>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
The area of a circle with radius 2.5 is 19.62

上述代码中,{r}被变量 r 的值替换,{s:.2f}被变量 s 的值替换,并且 : 后面的 .2f 指定了格式化参数(即保留两位小数),因此,{s:.2f}的替换结果是19.62

练习

小明的成绩从去年的 72 分提升到了今年的 85 分,请计算小明成绩提升的百分点,并用字符串格式化显示出'xx.x%',只保留小数点后 1 位:

s1 = 72
s2 = 85
r = ???
print('???')

参考源码

the_string.py

小结

Python 3 的字符串使用 Unicode,直接支持多语言。

strbytes互相转换时,需要指定编码。最常用的编码是UTF-8。Python 当然也支持其他编码方式,比如把 Unicode 编码成GB2312

>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'

但这种方式纯属自找麻烦,如果没有特殊业务要求,请牢记仅使用 UTF-8 编码。

格式化字符串的时候,可以用 Python 的交互式环境测试,方便快捷。

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-07发表,共计5045字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7994175
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
我用AI做了一个1978年至2019年中国大陆企业注册的查询网站

我用AI做了一个1978年至2019年中国大陆企业注册的查询网站

我用 AI 做了一个 1978 年至 2019 年中国大陆企业注册的查询网站 最近星哥在 GitHub 上偶然...
星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛 NAS-5:飞牛 NAS 中的 Docker 功能介绍 大家好,我是星哥,今天给大家带来如何在...
星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛 NAS-1:安装飞牛 NAS 前言 在家庭和小型工作室场景中,NAS(Network Atta...
免费领取huggingface的2核16G云服务器,超简单教程

免费领取huggingface的2核16G云服务器,超简单教程

免费领取 huggingface 的 2 核 16G 云服务器,超简单教程 前言 HuggingFace.co...
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸 前言 作为天天跟架构图、拓扑图死磕的...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛 NAS 硬件 02:某鱼 6 张左右就可拿下 5 盘位的飞牛圣体 NAS 前言 大家好,我是星...
三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

  三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Andr...
安装并使用谷歌AI编程工具Antigravity(亲测有效)

安装并使用谷歌AI编程工具Antigravity(亲测有效)

  安装并使用谷歌 AI 编程工具 Antigravity(亲测有效) 引言 Antigravity...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...
恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击 PHP-FPM(FastCGl Process M...