
2.2 字符串
大多数程序都定义并收集某种数据,然后使用它们来做些有意义的事情。鉴于此,对数据进行分类大有裨益。字符串虽然看似简单,但能够以很多不同的方式使用。
2.2.1 字符串的定义
字符串是一种用来表示文本的数据类型,它是由符号或者数值组成的一个连续序列。Python中的字符串是不可变的,一旦创建便不可修改。Python支持使用单引号、双引号和三引号定义字符串,其中,单引号和双引号通常用于定义单行字符串,三引号通常用于定义多行字符串与制作文档字符串。
1.定义单行字符串
在Python中,用引号括起的都是字符串,其中的引号可以是单引号,也可以是双引号,单引号和双引号的作用是等价的,如下所示:

定义字符串时单引号与双引号可以嵌套使用。需要注意的是,在使用双引号表示的字符串中允许嵌套单引号,但不允许包含双引号。这种灵活性让你能够在字符串中包含引号和撇号,例如:

此外,如果单引号或者双引号中的内容包含换行符,那么字符串会被自动换行。例如:

程序输出结果:

2.定义多行字符串
三引号在创建短字符串时没有什么特殊用处。它多用于创建多行字符串。使用三引号(三个单引号或者三个双引号)定义多行字符串时,在字符串中可以包含换行符、制表符或者其他特殊的字符。例如:

输出以上使用三引号定义的字符串,输出结果如下:

三引号是Python特有的语法,在三引号中可以输入单引号、双引号或换行符等字符:

代码的三引号中带有双引号,双引号也会被输出。输出结果如下:

3.制作文档字符串
三引号的另一种用法是制作文档字符串。Python的每个对象都有一个属性__doc__,这个属性用于描述该对象的作用,如示例4所示。
【示例4】 用三引号制作doc文档

运行代码,控制台输出结果如下:

4.转义字符
如果要输出含有特殊字符(单引号、双引号等)的字符串,需要使用转义字符。Python中的转义字符为“\”,和C、Java中的转义字符符号相同。转义操作只要在特殊字符的前面加上“\”即可。Python中的常用转义字符如表2-2所示。
表2-2 Python中的转义字符

下面这段代码说明了特殊字符的转义用法:

代码中的单引号是特殊字符,需要在“'”前加上转义字符。代码的输出结果如下:

使用双引号或三引号可以直接输出含有特殊字符的字符串,不需要使用转义字符:

这两行代码的输出结果均为:

代码中使用了双引号表示字符串变量str1,因此Python能够识别出该双引号内部的单引号是要输出的字符。代码使用三引号表示字符串变量str2,注意,最后一个单引号后面留有一个空格,这个空格是为了让Python识别出三引号留下的。如果不留下这个空格,4个单引号连在一起,Python解释器就不能正确识别三引号,将提示如下错误:

注意
输出的字符串中如果含有单引号,则需要使用双引号表示字符串;输出的字符串中如果含有双引号,则需要使用单引号表示字符串。
2.2.2 字符串的格式化输出
Python的字符串可通过占位符%、format()方法和f-strings三种方式实现格式化输出,下面分别介绍这三种方式。
1.占位符%
利用占位符%对字符串进行格式化时,Python会使用一个带有格式符的字符串作为模板,这个格式符用于为真实值预留位置,并说明真实值应该呈现的格式。例如:


一个字符串中可以同时含有多个占位符。例如:

上述代码首先定义了变量name与age,然后使用两个占位符%进行格式化输出,因为需要对两个变量进行格式化输出,所以可以使用“()”将这两个变量赋值存储起来。
不同的占位符为不同的变量预留位置,常见的占位符如表2-3所示。
表2-3 常见占位符

占位符%不仅可以通过格式化标记进行字符串格式化,还可以通过辅助标记实现精度控制输出,如示例5所示。
【示例5】 通过辅助标记实现精度控制输出

运行代码,控制台输出结果如下:

“%5.2f”:表示总长度为5(包含小数点),其中小数位长度为2;“%010.2f”:表示总长度为10(包含小数点),其中小数位长度为2,如果位数不足则补0。可以通过辅助标记实现浮点数据的显示,同时利用长度的限制方便地实现四舍五入功能。
使用占位符%时需要注意变量的类型,若变量类型与占位符不匹配,程序会产生异常。例如:

以上代码使用占位符%d对字符串变量age进行格式化,由于变量类型与占位符不匹配,因此出现了TypeError异常。
2.format()方法
format()方法同样可以对字符串进行格式化输出,与占位符%不同的是,使用format()方法不需要关注变量的类型。format()方法的基本使用格式如下:

在format()方法中,使用“{}”为变量预留位置。例如:


其中使用“{}”定义了最为基础的格式化数据占位标记,在进行数据填充时,只需要按照format()函数标记的顺序传入所需要的数据就可以形成最终所需要的内容。如果字符串中包含多个“{}”,并且“{}”内没有指定任何序号(从0开始编号),那么默认按照“{}”出现的顺序分别用format()方法中的参数进行替换;如果字符串的“{}”中明确指定了序号,那么按照序号对应的format()方法的参数进行替换。例如:

format()方法还可以对数字进行格式化,包括保留n位小数、数字补齐和显示百分比,下面分别进行介绍。
(1)保留n位小数。使用format()方法可以保留浮点数的n位小数,其格式为“{:.nf}”,其中n表示保留的小数位数。例如,变量pi的值为3.1415,使用format()方法保留2位小数:

上述示例代码中,使用format()方法保留变量pi的两位小数,其中“{:.2f}”可以分为“{:}”与“.2f”,{:}表示获取变量pi的值,“.2f”表示保留两位小数。
(2)数字补齐。使用format()方法可以对数字进行补齐,其格式为“{:m>nd}”,其中m表示补齐的数字,n表示补齐后数字的长度。例如,某个序列编号从001开始,此种编号可以在1之前使用两个“0”进行补齐:

上述示例代码中,使用format()方法对变量num的值进行补“0”操作,其中“{:0>3d}”的“0”表示要补的数字,“>”表示在原数字左侧进行补充,“3”表示补充后数字的长度。
(3)显示百分比。使用format()方法可以将数字以百分比形式显示,其格式为“{:.n%}”,其中n表示保留的小数位。例如,变量num的值为0.1,将num值保留0位小数并以百分比格式显示:

上述示例代码中,使用format()方法将变量num的值以百分比形式显示,其中“{:.0%}”的“0”表示保留的小数位。
3.f-strings
f-strings是从Python 3.6版本开始加入Python标准库的内容,它提供了一种更为简洁的格式化字符串方法。f-strings在格式上以f或F引领字符串,字符串中使用{}标明被格式化的变量。f-strings本质上不再是字符串常量,而是在运行时运算求值的表达式,所以在效率上优于占位符%和format()方法。
使用f-strings不需要关注变量的类型,但是仍然需要关注变量传入的位置。例如:

使用f-strings还可以进行多个变量格式化输出。例如:

2.2.3 字符串的常见操作
字符串在实际开发中会经常用到,掌握字符串的常用操作有助于提高代码编写效率。下面介绍针对字符串的常见操作。
1.字符串拼接
在Python中,可以使用“+”符号将多个字符串或字符串变量拼接起来,如下所示:

也可以直接将一个字面字符串(非字符串变量)放到另一个的后面直接实现拼接:

进行字符串拼接时,Python并不会自动添加空格,而需要显式定义。但当我们调用print()进行打印时,Python会在各个参数之间自动添加空格并在结尾添加换行符:

2.字符串替换
字符串的replace()方法可使用新的子串替换目标字符串中原有的子串,其语法格式如下:

上述语法中,参数old表示原有子串;参数new表示新的子串;参数count用于设置替换次数。使用replace()方法可实现字符串替换。例如:

如果在字符串中没有找到匹配的子串,会直接返回原字符串。例如:

3.使用*复制
使用*可以进行字符串复制。试着把下面几行代码输入到交互式解释器里,看看结果是什么:


运行代码,控制台输出结果如下:

4.使用[]提取字符
在字符串名后面添加[],并在括号里指定偏移量,可以提取该位置的单个字符。第一个字符(最左侧)的偏移量为0,下一个是1,以此类推。最后一个字符(最右侧)的偏移量也可以用-1表示,这样就不必从头数到尾。偏移量从右到左紧接着为-2、-3,以此类推。

如果指定的偏移量超过了字符串的长度(记住,偏移量从0开始增加到字符串长度-1),会得到一个异常提醒:

位置索引在其他序列类型(列表和元组)中的使用也是如此,你将在后面章节见到。
5.使用len()获得长度
到目前为止,我们已经学会了使用许多特殊的符号(如+)对字符串进行相应操作。但符号只有有限的几种。从现在开始,我们将学习使用Python的内置函数。所谓函数,指的是可以执行某些特定操作的有名字的代码。len()函数可用于计算字符串包含的字符数:

也可以对其他的序列类型使用len(),这些内容都将在序列中介绍。
6.字符串分割
与广义函数len()不同,有些函数只适用于字符串类型。为了调用字符串函数,你需要输入字符串的名称、一个点号,接着是需要调用的函数名,以及需要传入的参数:

使用内置的字符串函数split()可以基于分割符将字符串分割成由若干子串组成的列表。所谓列表(list),是由一系列值组成的序列,值与值之间由英文逗号隔开,整个列表被方括号所包裹。字符串的split()方法可以使用分割符把字符串分割成序列,该方法的语法格式如下:

在上述语法中,sep(分割符)默认为空字符,包括空格、换行(\n)、制表符(\t)等。如果maxsplit有指定值,则split()方法将字符串str分割为maxsplit个子串,并返回一个分割以后的字符串列表。
使用split()方法实现字符串分割。例如:

上面例子中,字符串名为todos,函数名为split(),传入的参数为单一的分割符','。如果不指定分隔符,那么split()将默认使用空白字符——换行符、空格、制表符。

即使不传入参数,调用split()函数时仍需要带着括号,这样Python才能知道你想要进行函数调用。
7.去除字符串两侧的空格
在程序中,额外的空格可能令人迷惑。对程序员来说,'python'和'python'看起来几乎没什么两样,但对程序来说,它们却是两个不同的字符串。Python能够发现'python'中额外的空格,并认为它是有意义的——除非你告诉它不是这样的。
空格很重要,因为你经常需要比较两个字符串是否相同。例如,一个重要的示例是,在用户登录网站时检查其用户名。但在一些简单得多的情形下,额外的空格也可能令人迷惑。所幸在Python中,删除用户输入的数据中多余的空格易如反掌。字符串对象的strip()方法一般用于去除字符串两侧的空格,该方法的语法格式如下:

strip()方法的参数chars用于设置要去除的字符,默认要去除的字符为空格。例如:

还可以指定要去除的字符为其他字符,如去除字符串两侧的“*”。代码如下:

8.使用join()合并
可能你已经猜到了,join()函数与split()函数正好相反:它将包含若干子串的列表分解,并将这些子串合成一个大的、完整的字符串。join()的调用顺序看起来有点别扭,与split()相反,你需要首先指定黏合用的字符串,然后再指定需要合并的列表:string.join(list)。因此,为了将列表lines中的多个子串合并成一个完整的字符串,我们应该使用语句:'\n'.join(lines)。下面的例子将列表中的名字通过逗号及空格黏合在一起:

9.大小写与对齐方式
我们的测试字符串如下所示:

将字符串末尾的省略号删除掉:

由于字符串是不可变的,上面这些例子实际上没有一个对setup真正做了修改。它们都仅仅是获取了setup的值,进行某些操作后将操作结果赋值给了另一个新的字符串而已。
将字符串首字母转换成大写:

将所有单词的开头字母转换成大写:

将所有字母都转换成大写:

将所有字母都转换成小写:

将所有字母的大小写都进行转换:

再来看看与格式排版相关的函数。这里,我们假设例子中的字符串被排版在指定长度(这里是30个字符)的空间里。30个字符位都居中:

左对齐:

右对齐:

10.使用replace()替换
由于字符串是不可变的,因此你无法直接插入字符或改变指定位置的字符。看看当我们试图将'Henny'改变为'Penny'时会发生什么:


为了改变字符串,我们需要组合使用一些字符串函数,如replace(),以及切片操作(很快就会学到):

使用replace()函数可以进行简单的子串替换。你需要传入的参数包括:需要被替换的子串、用于替换的新子串以及需要替换多少处。最后一个参数如果省略,则默认只替换第一次出现的位置:

修改最多100处:

当你准确地知道想要替换的子串是什么样子时,replace()是个非常不错的选择,但使用时一定要小心!在上面第二个例子中,如果我们粗心地把需要替换的子串写成了单个字符的'a'而不是两个字符的'a'(a后面跟着一个空格)的话,就会错误地将所有单词中出现的a一并替换:

有时,你可能想确保被替换的子串是一个完整的词或者某一个词的开头,等等。
11.使用制表符或换行符来添加空白
在编程中,空白泛指任何非打印字符,如空格、制表符和换行符。你可使用空白来组织输出,以使其更易读。要在字符串中添加制表符,可使用字符组合\t,如下述代码的❶处所示:

要在字符串中添加换行符,可使用字符组合\n:

还可在同一个字符串中同时包含制表符和换行符。字符串“\n\t”让Python换到下一行,并在下一行开头添加一个制表符。下面的示例演示了如何使用一个单行字符串来生成四行输出:

在接下来的两章中,你将使用为数不多的几行代码来生成很多行输出,届时制表符和换行符将提供极大的帮助。
2.2.4 字符串的索引与切片
在程序的开发过程中,可能需要对一组字符串中的某些字符进行特定的操作。Python可以通过字符串的索引与切片功能提取字符串中的特定字符或子串,下面分别对字符串的索引和切片进行讲解。
1.索引
字符串是一个由元素组成的序列,每个元素所处的位置是固定的,并且对应着一个位置编号,编号从0开始,依次递增1,这个位置编号被称为索引或者下标。
下面通过一张示意图来描述字符串的索引,如图2-1所示。图2-1中的索引自0开始从左至右依次递增,这样的索引称为正向索引;如果索引自-1开始,从右至左依次递减,则为反向索引。反向索引的示意图如图2-2所示。

图2-1 字符串的索引(正向)

图2-2 字符串的索引(反向)
通过索引可以获取指定位置的字符,其语法格式如下:

假设变量str_python的值为“python”,使用正向索引和反向索引获取该变量中的字符“p”。例如:

需要注意的是,当使用索引访问字符串值时,索引不能越界,否则程序会报索引越界的异常。
2.切片
切片操作(slice)可以从一个字符串中抽取子字符串(字符串的一部分)。我们使用一对方括号、起始偏移量start、终止偏移量end以及可选的步长step来定义一个切片。其中一些可以省略。切片得到的子串包含从start开始到end之前的全部字符。切片用于截取目标对象中的一部分,其语法格式如下:

切片的默认步长为1。需要注意的是,切片选取的区间属于左闭右开型,切下的子串包含起始位,但不包含结束位。
➢ [:]提取从开头到结尾的整个字符串。
➢ [start:]从start提取到结尾。
➢ [:end]从开头提取到end-1。
➢ [start:end]从start提取到end-1。
➢ [start:end:step]从start提取到end-1,每step个字符提取一个。
与之前一样,偏移量从左至右从0开始,依次增加;从右至左从-1开始,依次减小。如果省略start,则切片会默认使用偏移量0(开头);如果省略end,切片会默认使用偏移量1(结尾)。
我们来创建一个由小写字母组成的字符串:

仅仅使用[:]切片等价于使用[0:-1](也就是提取整个字符串):

下面是一个从偏移量20提取到字符串结尾的例子:

现在,从偏移量10提取到结尾:

下一个例子提取了偏移量从12到14的字符(Python的提取操作不包含最后一个偏移量对应的字符):

提取最后三个字符:

下面一个例子提取了从偏移量为18的字符到倒数第4个字符。注意与上一个例子的区别:当偏移量-3作为开始位置时,将获得字符x;而当它作为终止位置时,切片实际上会在偏移量-4处停止,也就是提取到字符w:

接下来,试着提取从倒数第6个字符到倒数第3个字符:

如果你需要的步长不是默认的1,可以在第二个冒号后面进行指定,就像下面几个例子所示。从开头提取到结尾,步长设为7:

从偏移量4提取到偏移量19,步长设为3:

从偏移量19提取到结尾,步长设为4:

从开头提取到偏移量20,步长设为5:

是不是非常方便?但这还没有完。如果指定的步长为负数,机智的Python还会从右到左反向进行提取操作。下面这个例子便从右到左以步长为1进行提取:

事实上,你可以将上面的例子简化为下面这种形式,结果完全一致:

切片操作对于无效偏移量的容忍程度要远大于单字符提取操作。在切片中,小于起始位置的偏移量会被当作0,大于终止位置的偏移量会被当作-1,就像接下来几个例子展示的一样。
提取倒数50个字符:

提取从倒数第51个字符到倒数第50个字符:

从开头提取到偏移量为69的字符:

从偏移量为70的字符提取到偏移量为71的字符:

2.2.5 技能训练
上机练习4字符串应用
需求说明
编写一个独立的程序,并将其保存为名称类似于name_cases.py的文件。
1.个性化消息:将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello Eric,would you like to learn some Python today?”。
2.调整名字的大小写:将一个人名存储到一个变量中,再以小写、大写和首字母大写的方式显示这个人名。
3.名言1:找一句你钦佩的名人说的名言,将这个名人的姓名和他的名言打印出来。输出应类似于下面这样(包括引号):Albert Einstein once said,“A person who never made a mistake never tried anything new.”
4.名言2:类似名言1,但将名人的姓名存储在变量famous_person中,再创建要显示的消息,并将其存储在变量message中,然后打印这条消息。
5.剔除人名中的空白:存储一个人名,并在其开头和末尾都包含一些空白字符。务必至少使用字符组合“\t”和“\n”各一次。打印这个人名,以显示其开头和末尾的空白。然后,分别使用剔除函数lstrip()、rstrip()和strip()对人名进行处理,并将结果打印出来。
上机练习5文本进度条
需求说明
进度条以动态方式实时显示计算机处理任务时的进度,它一般由已完成任务量与剩余未完成任务量的大小组成。编写程序,实现如图2-3所示的进度条动态显示的效果。

图2-3 文本进度条
上机练习6敏感词替换
需求说明
敏感词是指带有敏感政治倾向、暴力倾向、不健康色彩的词或不文明的词。大部分网站、论坛、社交软件都会使用敏感词过滤系统,考虑到该系统的复杂性,这里使用字符串中的replace()方法模拟敏感词过滤,将含有敏感词的语句使用“*”符号进行替换。
编写程序,实现替换语句中敏感词功能。