文章目录
- 🛫 系列文章导航
- 概述
- 本篇知识路线
- 再讲字符串
- 字符串元素的访问方式
- 字符串运算
- 操作字符串常用的函数
- 转义字符
- 字符编码
- 格式化
- 格式化方式怎么选
- 总结
- 参考资料
🛫 系列文章导航
- 【Python3基础】01-Python 环境与开发工具
- 【Python3基础】02-输入输出与程序运行
- 【Python3基础】03-基本数据类型与表达式
- 【Python3基础】04-字符串与格式化
- 【Python3基础】05-容器数据类型
- 【Python3基础】06-条件判断与循环
- 【Python3基础】07-函数基础与参数设计
- 【Python3基础】08-高阶函数、闭包与装饰器
- 【Python3基础】09-切片、推导式与迭代协议
- 【Python3基础】10-类与对象
- 【Python3基础】11-继承、多态与对象协议
- 【Python3基础】12-动态类型与元编程
- 【Python3基础】13-Python 常用设计模式
- 【Python3基础】14-模块、包与虚拟环境
- 【Python3基础】15-文件、目录与内存流
- 【Python3基础】16-序列化、异常与调试
- 【Python3基础】17-常用标准库与第三方库
- 【Python3基础】18-多进程、多线程与协程
- 【Python3基础】19-Socket 与 TCP、UDP 编程
- 【Python3基础】20-网络应用与异步 IO
概述
字符串不只是文字,它还是程序与用户、文件和网络交换信息的主要载体。
本篇集中讲解字符串的索引、遍历、常用方法、转义与格式化。字符串是不可变对象,对它执行替换、拼接等操作时,通常会得到一个新字符串。理解这一点,能避免很多看似修改成功、实际原值不变的问题。
本篇知识路线
字符串: P y t h o n 正索引: 0 1 2 3 4 5 负索引: -6 -5 -4 -3 -2 -1再讲字符串
在前面基本数据类型一节我们学过字符串类型,字符串类型严格意义上来说不属于基本数据类型, 因为字符串是由多个单字符类型的数据组成的复合类型。
但 Python 不支持单字符类型,单字符在 Python 中也是作为一个字符串使用,这个和其它语言不同, Python 语言内置字符串类型,字符串是Python中最常用的数据类型之一。
我们本节课再次深入学习一下字符串的特点和函数,以及编码格式。
字符串元素的访问方式
字符串是有序集合,有序集合并不是集合里面的数据是有序的, 有序集合的意思是可以通过下标访问的集合。我们可以通过下标访问字符串,注意第一个元素是从下标 0 开始,越界访问 Python 解释器会报异常错误。
strone="老鸟Python"print(strone[0])print(strone[100])# 越界访问,会报异常字符串是个常量,无论通过何种运算,都不会修改字符串本身的值, 我们不能通过该变量的索引修改它所指向的字符串内的字符, 但我们可以改变该变量指向的字符串。
strone="老鸟Python"strone[0]="H"# 错误,不可以修改字符串本身的值strone="Hello World"# 正确,可以修改变量 strone 的指向print(strone)字符串运算
字符串支持一系列的运算,但遵守一定的规则,我们列举出常用的字符串运算,如下表格。
| 运算符 | 解释 | 运算规则 |
|---|---|---|
| 字符串连接 | 只能是字符串和字符串相加 | |
| 扩大字符串的倍数 | 只能是整数和字符串相乘(负整数和字符串相乘输出空字符串) [] | 通过索引访问字符串内元素 |
重要的事情再说一遍,无论什么运算符操作字符串,都不会修改字符串本身的值。
strone="Hello"newstr=strone[0:3]# 返回索引 0 到 3(不包含3)的子串print(strone)print(newstr)操作字符串常用的函数
操作字符串的函数,有字符串本身内置的函数,也有非内置的函数, 这些函数可以对字符串进行一系列的操作,下面表格是字符串常用的一些函数。
| 函数 | 解释 | 性质 |
|---|---|---|
| len | 计算字符串的长度并返回(不是字节长度) | Python 内置函数 |
| find | 字符串内查找子串,并返回子串首字母所在的索引,否则返回 -1 | 字符串自带函数 |
| replace | 字符串内查找子串并返回替换后的字符串 | 字符串自带函数 |
| lower | 把字符串内字符串内的所有字母变为小写并返回 | 字符串自带函数 |
| upper | 把字符串内字符串内的所有字母变为大写并返回 | 字符串自带函数 |
重要的事情说三遍,无论什么函数操作字符串,都不会修改字符串本身的值。
strone="老鸟Python"newstr=strone.replace("Python","Hello")print(strone)print(newstr)strtwo="Hello World"lowerstr=strtwo.lower()print(strtwo)print(lowerstr)strthree="Bird Python"upperstr=strthree.upper()print(strthree)print(upperstr)转义字符
在编程语言里,有很多特殊字符,它们起着各种各样的作用。 有些特殊字符没有办法用普通字符表示,需要进行转义。 python用反斜杠 \ 转义字符,下表列举了一些常用的需要转义的字符。
| 常用的转义字符 | 解释 | 适用方式 |
|---|---|---|
| \ | 反斜杠 | 单引号,双引号,三引号 |
| ’ | 单引号 | 单引号,双引号,三引号 |
| " | 双引号 | 单引号,双引号,三引号 |
| \t | 制表符 | 单引号,双引号,三引号 |
| \n | 换行 | 单引号,双引号,三引号 |
注意:Python 中可以用 r 来禁止字符转义,试一试下面的代码。
strone=r"H\te\rl\nl\"o\'"print(strone)字符编码
由于计算机是美国人发明的,因此,最早只有 127 个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为 ASCII 编码(范围为 0127),比如字符 A 的编码是 65,字符 1 的编码是 49。我们知道 1 个字节可以表示的范围是 0255,所以我们只需要 1 个字节既可以表示出来所有的 ASCII 编码。
strone="A"print(ord(strone))# ASCII 值为 65;用 1 个字节的 2 进制表示为:01000001strtwo="1"print(ord(strtwo))# ASCII 值为 33;用 1 个字节的 2 进制表示为:00100001但是要处理中文用 1 个字节显然是不够的,至少需要 2 个字节, 用两个字节表示一个字符(包括英文,数字,可见字符等)的编码,我们称为 UNICODE 编码。我们知道 2 个字节的表示的范围为 0~65535,注意,我们在字符串前面加上 u 来告诉计算机用 UNICODE 编码方式存储字符串。
strone=u"A"print(ord(strone))# UNICODE 值为 65;用 2 个字节的 2 进制表示为:00000000 01000001strtwo=u"牛"print(ord(strtwo))# UNICODE 值为 29275;用 2 个字节的 2 进制表示为:01110010 01011011我们来分析一下上面的例子:汉字“牛”对应的十进制为 29275,显然已经超出了 ASCII 编码的范围,所以用 1 个字节无法表示,我们只能用两个字节的 UNICODE 编码方式,对应的二进制为 01110010 01011011。字母 A 对应的十进制为 65,可以使用 1 个字节的 ASCII 编码方式,对应的二进制为 01000001,当然我们也可以使用 2 个字节的 UNICODE 编码方式,对应的进制为 00000000 01000001。我们来考虑一个问题,如果我们写的字符串文本只有极个别中文,而大部分都是英文,因为有中文的存在,我们无法使用 ASCII 编码方式进行统一编码,只能使用 UNICODE 编码方式,而采用 UNICODE 编码比 ASCII 编码需要多一倍的存储空间,在存储和传输上就十分不划算,如果有一种编码可以智能的对文本中的英文采用 ASCII 编码方式,对中文采用 UNICODE 编码方式那该多好啊,有的,就是 UTF-8 编码。UTF-8 编码把一个 UNICODE 字符根据不同的数字大小编码成 1-6 个字节,比如,如果是常用的英文字符、数字字符、英文符合字符等都被编码成 1 个字节,一般汉字通常是 3 个字节,只有很生僻的字符才会被编码成 4-6 个字节。在 Python 3 中,字符串前面什么都不要加,默认就是采用 UTF-8 编码;如果加上 u 就是 UNICODE 编码。
strone = "you" # UTF-8 编码,共占用 3 个字节(和 ASCII 编码一样 1 个英文字符 1 个字节) strtwo = "牛掰" # UTF-8 编码,共占用 6 个字节(1 个中文字符通常是 3 个字节) strthree = "you牛掰" # UTF-8 编码,共占用 9 个字节(1 个英文字符 1 个字节,1 个中文字符通常是 3 个字节) strfour = u"you" # UNICODE 编码,共占用 6 个字节(1 个英文字符 2 个字节) strfive = u"牛掰" # UNICODE 编码,共占用 4 个字节(1 个中文字符 2 个字节) strsix = u"you牛掰" # UNICODE 编码,共占用 10 个字节(1 个英文字符 2 个字节,1 个中文字符 2 个字节)Python 内置的 len 函数可以计算字符串的长度,但是这个长度是什么意思呢,我们通过查看 len 函数的源码发现,它内部逻辑有个判断:
如果计算的字符串是 UNICODE 编码方式,则返回值是:字符串所占的字节数除以 2。
如果字符串是 UTF-8 编码,对于字符串里面有 ASCII 字符则按字节计算,对于字符串里面的中文字符,则按该中文字符所占的字节数除以编码该中文字符所用的字节数 。
strone="you"# UTF-8 编码,共占用 3 个字节(和 ASCII 编码一样,每个英文字符占 1 个字节)strtwo="牛掰"# UTF-8 编码,共占用 6 个字节(每个中文字符通常占 3 个字节)strthree="you牛掰"# UTF-8 编码,共占用 9 个字节(每个英文字符占 1 个字节,每个中文字符通常占 3 个字节)print(len(strone))# 3(1 + 1 + 1)print(len(strtwo))# 2(3/3 + 3/3)print(len(strthree))# 5(1 + 1 + 1 + 3/3 + 3/3)strfour=u"you"# UNICODE 编码,共占用 6 个字节(每个英文字符占 2 个字节)strfive=u"牛掰"# UNICODE 编码,共占用 4 个字节(每个中文字符占 2 个字节)strsix=u"you牛掰"# UNICODE 编码,共占用 10 个字节(每个英文字符占 2 个字节,每个中文字符占 2 个字节)print(len(strfour))# 3(2/2 + 2/2 + 2/2)print(len(strfive))# 2(2/2 + 2/2)print(len(strsix))# 5(2/2 + 2/2 + 2/2 + 2/2 + 2/2)Python 内置的 encode 函数可以把字符串类型转换为字节类型,encode 的参数决定对字符进行编码时,每个字符使用几个字节。
strone="you牛掰"print(type(strone))# str 类型print(len(strone))# 5(1 + 1 + 1 + 3/3 + 3/3)strtwo=strone.encode("utf8")# 按 utf8 方式编码(每个 ascii 占 1 个字节,每个中文占 3 个字节)print(type(strtwo))# bytes 类型print(strtwo)# b'you\xe7\x89\x9b\xe6\x8e\xb0'print(len(strtwo))# 9(1 + 1 + 1 + 3 + 3)strthree=strone.encode("gbk")# 按 gbk 方式编码(每个 ascii 占 1 个字节,每个中文占 2 个字节)print(type(strthree))# bytes 类型print(strthree)# b'you\xc5\xa3\xea\xfe'print(len(strthree))# 7(1 + 1 + 1 + 2 + 2)说明:gbk 编码是微软出的对含有中文的字符串进行编码的一种格式,gbk 对 ascii 使用 1 个字节,对中文使用 2 个字节。
注意:我们对字符串使用 encode 编码后的字节是由 encode 的参数决定的,而不是由被编码的字符是什么编码格式决定的, 比如,大家可以把strone = "you牛掰"改成strone = u"you牛掰",做一下验证,结果和上面是一样的。
同样,我们可以使用 decode 函数,把字节类型数据解码成字符串类型。
strone=b'you\xe7\x89\x9b\xe6\x8e\xb0'print(type(strone))# bytes 类型print(len(strone))# 9(1 + 1 + 1 + 3 + 3)strtwo=strone.decode("utf8")# 按 utf8 方式解码(每个 ascii 转为 1 个字符,每 3 个非 ascii 转为 1 个中文)print(type(strtwo))# str 类型print(strtwo)# you牛掰print(len(strtwo))# 5(1 + 1 + 1 + 3/3 + 3/3)strone=b'you\xc5\xa3\xea\xfe'strthree=strone.decode("gbk")# 按 gbk 方式解码(每个 ascii 转为 1 个字符,每 2 个非 ascii 转为 1 个中文)print(type(strthree))# str 类型print(strthree)# you牛掰print(len(strthree))# 5(1 + 1 + 1 + 2/2 + 2/2)格式化
我们知道 print 函数会把输出的各种类型数据全变成字符串输出到屏幕上, 当其中输出的语句内容有变量的时候,我们需要根据这些变量的类型进行格式化。
name="如花"age=18print("%s的年龄是%d"%(name,age))对应的被格式化的变量类型对应的占位符一般如下表。
| 占位符 | 被格式化成的类型 | 被格式化的数据类型 |
|---|---|---|
| %d | 整数 | int,float,bool |
| %f | 浮点数 | int,float,bool |
| %s | 字符串 | 所有类型 |
| %x | 十六进制整数 | int,float,bool |
格式化方式怎么选
已有代码中会同时见到%、str.format()和 f-string。阅读旧代码时三种方式都要能看懂;自己编写 Python 3 代码时,可以优先选择结构清楚的 f-string。无论采用哪一种方式,都不要用字符串拼接去构造需要参数化处理的数据库语句或系统命令。
总结
- 字符串不可变;索引、切片和常用方法会得到结果,但不会原地修改原字符串。
len()返回字符串中的字符数,显示宽度可能不同。 - 根据用途选择转义字符、多行字符串和格式化方式;拼接或转换后仍应检查结果是否符合预期。
- 比较字符串内容用
==,is比较对象身份。可以对原字符串和lower()的结果分别使用这两个运算符,观察差别。
参考资料
- 再讲字符串(原始网页)