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

Python 基础系列--字符串与编码

作者头像
somenzz
发布2020-12-10 11:25:30
8410
发布2020-12-10 11:25:30
举报
文章被收录于专栏:Python七号Python七号

一旦走上编程这条路,如果不把编码这个问题搞清楚,那么它会像幽灵般纠缠你整个职业生涯。

字符串在编程中是使用频率最高的数据类型,像 web 网站中显示的中英文信息,使用记事本打开一个文本文件所看到的内容,软件呈现给用户的信息,包括你现在看到的文字,都属于字符串,可以说字符串无处不在。如果对字符串的编码不理解,编辑过程中非常容易出现乱码问题,相反如果懂编码,那么即使出现乱码,也可以自助解决。不同的编程语言对字符串的处理可能略有差异,但对字符串的编码原理却是相通的, 因此字符串和编码是每个准程序员必备知识,需要引起重视。

先说Python 字符串,再说编码

来自维基百科关于字符串的定义:

字符串(String),是由零个或多个字符组成的有限串行。一般记为s=a[1]a[2]…a[n]。

比如为众多程序员所周知的 "hello, world!"就是一个字符串,其实不管中文英文,能写出来让人们看到的信息都属于字符串。python3 中的 print() 函数用于打印字符串(在 python2 中 print 是一个命令,可以不带括号)

代码语言:javascript
复制
>>> s1="hello,world!"
>>> s2="世界,你好!"
>>> type(s1)
<class 'str'>
>>> type(s2)
<class 'str'>
>>> print(s1)
hello,world!
>>> print(s2)
世界,你好!
>>>

上文中定义了两个字符串变量 s1,s2,注意 python 是动态语言,不需要事先声明变量的类型,变量的类型由其实际的值决定,运行时可动态改变,是不是非常灵活?!查看变量的类型使用 type() 函数,这个在 debug时请经常使用。

一、Python 中的字符串

python 对字符串的处理非常灵活,对字符串常用到的操作有:

1.定义字符串

python中可以使用单引号',双绰号",三引号(三个单引号'''或三个双引号"""来定义一个字符串,其中三引号可方便的定义多行文本。如下所示:

代码语言:javascript
复制
>>> s='你好,欢迎来到清如許的公众号。'   #定义一个字符串s ,下同
>>> print(s)                                                   #打印字符串s,下同
你好,欢迎来到清如許的公众号。
>>> s="你好,欢迎来到清如許的公众号。"
>>> print(s)
你好,欢迎来到清如許的公众号。
>>> s='''你好,欢迎来到清如許的公众号。'''
>>> print(s)
你好,欢迎来到清如許的公众号。
>>> s="""你好,欢迎来到清如許的公众号。"""
>>> print(s)
你好,欢迎来到清如許的公众号。
>>> s="""你好, #定义多行文本,打印时按定义时的格式输出
... 欢迎来到清如許的公众号。"""
>>> print(s)
你好,
欢迎来到清如許的公众号。
>>> s='你好,欢迎来到"清如許"的公众号。'   #如果字符串中出现单引号、双引号,那么定义时不能使用相同的引号
>>> print(s)
你好,欢迎来到"清如許"的公众号。
>>> s="""你好,欢迎来到"清如許"的公众号。"""    #如果字符串中出现单引号、双引号,那么定义时不能使用相同的引号
>>> print(s)
你好,欢迎来到"清如許"的公众号。
>>> s="你好,欢迎来到\"清如許\"的公众号。"    #如果字符串中出现单引号、双引号,如果使用相同的引号定义,那么要使用\转义。
>>> print(s)
你好,欢迎来到"清如許"的公众号。
>>> s='欢迎\                               #如果在定义时一行写不下,可以使用\连接下一行,它们仍然是一行字符串
... 来到清如許的公众号'
>>> print(s)
欢迎来到清如許的公众号
>>> s='你好,\n欢迎来到清如許的公众号。'     #如果使用单引号或双引号打印多行文本,使用\n做为换行符
>>> print(s)
你好,
欢迎来到清如許的公众号。
>>>

这里需要注意的是如果字符串中含有单引号或双引号时,有两种方法处理:转义或使用与字符串中引号不同的引号来定义。 如果需要避免转义,我们可以使用原始字符串,即在字符串的前面加上’r’。如:

代码语言:javascript
复制
>>> s = r"This is a rather long string containing\n\
... several lines of text much as you would do in C."
>>> print(s)
This is a rather long string containing\n\
several lines of text much as you would do in C.

2. 切片

python 中字符串其实是一个只读的数组,我们可以通过下标来访问字符串中的任意一个字符,请看下面交互式环境中的操作和注释:(交互式环境中的语句可以保存在后缀为.py文件中当作 python 程序来执行,类似shell 语法)

代码语言:javascript
复制
>>> s1="hello,world!"
>>> s2="世界,你好!"
>>> s1[0]     #python数据的下标从0开始,0表示字符串中第一个字符
'h'
>>> s1[1]
'e'
>>> s1[2]
'l'
>>> s1[-1]  # -1表示字符串中倒数第一个字符,是不是很容易记忆?!
'!'
>>> s1[-2]  # -2表示字符串中倒数第一个字符,是不是很容易记忆?!
'd'
>>> s2[0]   #中文字符串,也同样适用
'世'
>>> s2[1]
'界'
>>> s2[-1]
'!'

3.格式化字符串

格式化字符串的目的为了更方便打印字符串,先看一个例子: 文件名 lx_str_format.py

代码语言:javascript
复制
#encoding=utf-8
yourname = "农夫三拳"

game = "王者荣耀"

num = 100

rate = 0.81236    # 注意 rate 最终的打印显示

welcome_string = f"你好,{yourname},欢迎来到{game},你已获得{num}次 MVP,平均胜率 {rate:.2%}"
welcome_string1 = "你好,{},欢迎来到{},你已获得{}次 MVP,平均胜率 {:.2%}".format(
    yourname, game, num, rate
)
welcome_string2 = "你好,{0},欢迎来到{1},你已获得{2}次 MVP,平均胜率 {3:.2%}".format(
    yourname, game, num, rate
)
welcome_string3 = "你好,{3},欢迎来到{0},你已获得{1}次 MVP,平均胜率 {2:.2%}".format(
    game, num, rate, yourname
)
welcome_string4 = "你好,%s,欢迎来到%s,你已获得%d次 MVP,平均胜率 %.2f%%" % (yourname, game, num, rate*100)

print(welcome_string)
print(welcome_string1)
print(welcome_string2)
print(welcome_string3)
print(welcome_string4)

这里使用了三种方法来格式化字符串:

(1)welcome_string 使用 F-strings,是 Python3.6 版本新引入的特性,是最简洁易读,效率也是最高的。 (2)welcome_string1,welcome_string2,welcome_string3 都使用了字符串的 format 函数来进行格式化,通过不同的索引来引用 format 函数的参数。 (3)welcome_string4 使用 % 来格式化字符串,类似C语言中的 printf 函数,不再详述。 三种方法都对 rate 做了 %的转换,并保留两位小数,可以看到在{}使用 ‘:.2%’ 即可显示两位的百分比。上述代码执行结果如下所示:

代码语言:javascript
复制
你好,农夫三拳,欢迎来到王者荣耀,你已获得100次 MVP,平均胜率 81.24%
你好,农夫三拳,欢迎来到王者荣耀,你已获得100次 MVP,平均胜率 81.24%
你好,农夫三拳,欢迎来到王者荣耀,你已获得100次 MVP,平均胜率 81.24%
你好,农夫三拳,欢迎来到王者荣耀,你已获得100次 MVP,平均胜率 81.24%
你好,农夫三拳,欢迎来到王者荣耀,你已获得100次 MVP,平均胜率 81.24%

其中:F-strings 快速、易学、实用,能有效减少代码量,在实际使用中能使用 F-strings,能看懂其他两种方法即可

一些细节: 由于 F-strings 是在运行时进行渲染的,因此可以将任何有效的 Python 表达式放入其中。这可以让你做一些漂亮的事情,如:

代码语言:javascript
复制
>>> f"{2 * 37}"
'74'
>>> f"{'lower'.upper()}"
'LOWER'
>>>

为了使字符串出现大括号,您必须使用双大括号,如果使用三个以上的大括号,则可以获得更多大括号:

代码语言:javascript
复制
>>> f"{{'hello'}}"
"{'hello'}"
>>> f"{{'hello'}}"*3  #字符串乘以几表示重复几次
"{'hello'}{'hello'}{'hello'}"
>>> f"{{{{{{74}}}}}}"  #第两个大括号输出一个大括号
'{{{74}}}'

打印一个整数的二进制、八进制、十六进制

代码语言:javascript
复制
>>> f"十进制:{11},二进制:{11:b},八进制:{11:o},十六进制:{11:x}"
'十进制:11,二进制:1011,八进制:13,十六进制:b'
>>> f"十进制:{11},二进制:{11:#b},八进制:{11:#o},十六进制:{11:#x}"
'十进制:11,二进制:0b1011,八进制:0o13,十六进制:0xb'

对齐操作

代码语言:javascript
复制
>>> s
'a'
>>> f"{s.center(10)}"  # 共10位,字符串s居中显示,默认以空格填充
'    a     '
>>> f"{s.center(10,'*')}"  # 共10位,字符串s居中显示,指定以'*'填充
'****a*****'
>>> f"{s.ljust(10,'*')}"  # 共10位,字符串s靠左对齐,指定以'*'填充
'a*********'
>>> f"{s.rjust(10,'*')}" # 共10位,字符串s靠右对齐,指定以'*'填充
'*********a'
>>> num=10
>>> f"{num:5d}"      # 整数对齐,默认以空格填充,右对齐
'   10'
>>> f"{num:f}"   # 以浮点数据显示
'10.000000'
>>> f"{num:.3f}"  # 以浮点数据显示3位小数
'10.000'
>>> num=1234567890
>>> f"{num:,}"
'1,234,567,890'        #智能显示大数字。

4.其他对象转字符串

在实际应用中,将数据(整数,浮点数据)转为字符串的需求是非常频繁的,python3 中有两种方法将其他对象转为字符串:repr(object),str(object)

代码语言:javascript
复制
>>> repr(49)
'49'
>>> str(49)
'49'
>>> repr(49.99)
'49.99'
>>> str(49.99)
'49.99'
>>> repr(-49.99)
'-49.99'
>>> str(-49.99)
'-49.99'
>>> repr("hello,world")
"'hello,world'"
>>> str("hello,world")
'hello,world'

大多数情况下,这二者没有区别,函数 str() 用于将值转化为适于人阅读的形式,而 repr() 转化为供解释器读取的形式,如果一个对象没有适于人阅读的解释形式的话,str() 会返回与 repr() 等同的值。很多类型,诸如数值或链表、字典这样的结构,针对各函数都有着统一的解读方式。字符串和浮点数,有着独特的解读方式。因此,作为初学者还是使用 str() 函数吧。

5. 其他常见操作

(1)遍历: 不需要下标

代码语言:javascript
复制
>>> s2
'世界,你好!'
>>> for s in s2:
...     print(s)
...
世
界
,
你
好
!

需要下标:

代码语言:javascript
复制
>>> for i,s in enumerate(s2): #这是一种比较高效的方法,尽量不要使用 len(s0)。
...     print(f"s2[{i}] = {s}")
...
s2[0] = 世
s2[1] = 界
s2[2] = ,
s2[3] = 你
s2[4] = 好
s2[5] = !

(2)判断字符是否在字符串中:

代码语言:javascript
复制
>>> 'a' in 'ab'
True
>>> 'c' in 'ab'
False
>>> if 'a' in 'ab':
...     print("a is ab")
...
a is ab

(3)判断字符串中是否以某个字符串开始或结尾:

代码语言:javascript
复制
>>> "abcd".startswith("a")
True
>>> "abcd".endswith("cd")
True
>>>

(4)大小写转换、查找、替换、对齐、编码、分隔、去空等等,不再一一列举,使用时 help(str) 查找帮助即可。

代码语言:javascript
复制
help(str)

二、字符串编码

我们都知道计算机底层只能处理数字,也就是 0 和 1,因此任何文件存储在磁盘上都是 0 和 1 的二进制流。我们看到的字符串都是这些二进制流经过一定的规则转化而来的。比如小写字母 'a',根据美国信息交换标准代码,即按 ASCII 码编码时,对应的十进制为整数 97,十六进制为 61 ,二进制为 1100001。保存在磁盘时,它就变成了二进制流 1100001,当从磁盘中读取文件时,1100001 按 ASCII 码解码,会转为 'a' 呈现在我们眼前。过程简写如下:

字符串------->编码------->二进制流 二进制流------->解码------->字符串

计算机在设计时就使用一个字节表示 8 位二进制位,因此我们称这里的二进制流称为字节串,即:

写文件:字符串------->编码------->字节串(在磁盘) 读文件:字节串------->解码------->字符串 (在内存)

注意:字符串是存储在内存中的,二进制流/字节是存储在硬盘或网络数据流中。

由于 ASCII 编码只占用一个字节,即 8 个二进制位,共有 2 的 8 次方个,也就是 256 种可能,完全可以覆盖英文大小写字符及特殊符号。而我们中文汉字远超过256个,使用 ASCII 编码的一个字节来处理中文显然是不够用的,于是中国制定了 GB2312 编码,使用两个字节,可以支持共 2 的 16 次方共 65536 种汉字,可以覆盖常用的中文汉字60370个(当代的《汉语大字典》(2010年版) 收字60,370个)及 ASCII 码。

比如 "清如许" 这个字符串以 GB2312 编码后的字节串如下所示:

代码语言:javascript
复制
>>> "清如许".encode('gb2312')  # 以 GB2312 编码(encode)得到
b'\xc7\xe5\xc8\xe7\xd0\xed'
>>> list("清如许".encode('gb2312'))  # list将其转为列表/数组,方便十进制查看
[199, 229, 200, 231, 208, 237]
>>>
>>> b'\xc7\xe5\xc8\xe7\xd0\xed'.decode('gb2312') # 字节串以 GB2312 解码(decode)得到字符串
'清如许'

我们可以看到 "清如许"在 GB2312 编码中共占用了 6 个字节,当然 GB2312 也是包含 ASCII 码的:

代码语言:javascript
复制
>>> "abc".encode('gb2312')  #编码
b'abc'
>>> list("abc".encode('gb2312'))     #编码  ,十进制查看
[97, 98, 99]
>>> hex(97)
'0x61'
>>> hex(98)
'0x62'
>>> hex(99)
'0x63'
>>> b'\x61\x62\x63'.decode('gb2312')   #解码
'abc'

这仅仅是适用中文简体的一个编码,全世界有上百种语言,每个语言都设计自己独特的编码,这样计算机在跨语言进行信息传输时还是无法沟通(出现乱码)的,于是 Unicode 编码应运而生,Unicode 使用 2-4 个字节编码,已经收录136690个字符,并还在一直不断扩张中。 Unicode 起到了 2 个作用:

  • 直接支持全球所有语言,每个国家都可以不用再使用自己之前的旧编码了,用 Unicode 就可以了。(就跟英语是全球统一语言一样)。
  • Unicode 包含了跟全球所有国家编码的映射关系。

所有的系统、编程语言都默认支持 Unicode 。 Unicode 编码虽然统一了不同语言的编码不一致的问题,但是新的问题又来了,如果一段纯英文文本,用 Unicode 编码存储会比用 ASCII 编码多占用一倍空间!存储和网络传输时一般数据都会非常多,那么增加一倍空间是无法容忍的,为了解决上述问题,UTF 编码应运而生,UTF 编码将一个 Unicode 字符编码成 1~6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成 4~6 个字节。注意,从 Unicode 到 UTF 并不是直接的对应,而是通过一些算法和规则来转换的。

  • UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多 4 个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个。
  • UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
  • UTF-32: 使用4个字节表示所有字符。

可以看出 UTF-8 编码是最节省存储的,也是目前最常用的编码,很多网页的源码上会有类似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码。

代码语言:javascript
复制
>>> list("清如许".encode('utf8'))
[230, 184, 133, 229, 166, 130, 232, 174, 184]
>>> list("abc".encode('utf8'))
[97, 98, 99]
>>>

从上面的输出可以看出,中文在 utf8 编码中占用 3 个节点,英文还是占用 1 个字节,因此如果是中文文本以 utf8 编码保存占用的磁盘空间是 gb2312 编码保存的 1.5 倍。

编码的问题理解了,我们再来看下 Python3 代码的执行过程。

首先 Python3 解释器找到源代码文件,按源代码文件声明的编码方式解码内存,再转成 unicode 字符串。 把 unicode 字符串按照语法规则进行解释并执行,其中所有的变量字符都会以 unicode 编码声明。 读写文件过程如下图所示:

python源代码的编码解码过程

下面在 windows 上做个测试 编写 bm_test.py 保存为 utf8 编码,如下图所示:

bm.png

只要文件头部声明的编码和文件保存的编码一致,输出不会有乱码,推荐大家在编码过程都这样操作。

执行结果

但是如果 bm_test.py 不声明 # -- coding: utf-8 --,在默认编码为 gbk 的 windows 上执行仍会正常输出,这是因为到了内存里 python3 解释器把 utf-8 转成了 unicode , 但是这只是 python3, 并不是所有的编程语言在内存里默认编码都是 unicode ,比如 python2 的默认编码是 ascii ,python2 解释器仅以文件头声明的编码去解释你的代码,上述 bm_test.py 在 python2 中会以 utf-8 解码得到 utf-8 字符串,不会自动转为 unicode 字符串,这意味着在默认编码为 gbk 的 windows 上执行结果是乱码。

因为只有2种情况 ,你的 windows上显示才不会乱 (1)字符串以 GBK 字符串显示 (2)字符串是 unicode 编码 那么在 python2 中,需要你手工转换,在 windows 修改 bm_test.py 如下所示:

python2 中的 bm_test.py 执行结果

python 2 的执行结果

可以看出 Python3 容忍你的偷懒,而 Python2 却不行,还需要你手工转换,Python3 在编码方面比 Python2 是有明显进步的,建议初学者从 Python3 开始学习。 Python2 将在 2020 年停止更新。

总结:Python3 对字符串的处理是非常灵活的,有许多操作都可以一行代码完成,换成其他语言可能需要多写很多代码,如果了解关于字符串的详细信息,请使用 help(str) 来查询;对于字符编码问题,还是需要深入理解才行,这样在遇到任何编码的问题都可以迎刃而解。

如有读者针对文中内容有疑问,欢迎微信后台留言,会尽快回复。

(完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Python 中的字符串
    • 1.定义字符串
      • 2. 切片
        • 3.格式化字符串
          • 4.其他对象转字符串
            • 5. 其他常见操作
            • 二、字符串编码
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档