2. 字符串
所谓字符串,就是由零个或多个字符组成的有限序列。
我们一般使用引号(单引号、双引号和三引号都可以)来创建字符串。字符串是由一个一个的字符组成的。注意:字符串中可能会包含一种特殊字符——转义符。
s1 = 'hello, world!'
s2 = "你好,世界!❤️"
s3 = '''hello,
wonderful
world!'''
print(s1)
print(s2)
print(s3)
▶ 运行结果:
hello, world!
你好,世界!❤️
hello,
wonderful
world!
我们可以在字符串中使用\(反斜杠)来表示转义,也就是说\后面的字符不再是它原来的意义,例如:\n不是代表反斜杠和字符n,而是表示换行;\t也不是代表反斜杠和字符t,而是表示制表符。所以如果字符串本身又包含了'、"、\这些特殊的字符,必须要通过\进行转义处理。例如要输出一个带单引号或反斜杠的字符串,需要用如下所示的方法。
s1 = '\'hello, world!\''
s2 = '\\hello, world!\\'
print(s1)
print(s2)
▶ 运行结果:
'hello, world!'
\hello, world!\
Python 中有一种以r或R开头的字符串,这种字符串被称为原始字符串,意思是字符串中的每个字符都是它本来的含义,没有所谓的转义字符。例如,在字符串'hello\n'中,\n表示换行;而在r'hello\n'中,\n不再表示换行,就是字符\和字符n。大家可以运行下面的代码,看看会输出什么。
s1 = '\\it \\is \time \to \read \now'
s2 = r'\it \is \time \to \read \now'
print(s1)
print(s2)
▶ 运行结果:
\it \is ime o
ead
ow
\it \is \time \to \read \now
Python 中还允许在\后面还可以跟一个八进制或者十六进制数来表示字符,例如\141和\x61都代表小写字母a,前者是八进制的表示法,后者是十六进制的表示法。另外一种表示字符的方式是在\u后面跟Unicode字符编码,例如\u9a86\u660a代表的是中文"骆昊"。运行下面的代码,看看输出了什么。
s1 = '\141\142\143\x61\x62\x63'
s2 = '\u7a0b \u5e8f \u8bbe \u8ba1'
print(s1)
print(s2)
▶ 运行结果:
abcabc
程 序 设 计
对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:
print(ord('A')) # 65 这是ascII码的十进制数
print(ord('中')) # 20013 这是Unicode 编码对应的十进制数
print(chr(66)) # 'B'
print(chr(25991)) # '文'
▶ 运行结果:
65
20013
B
文
Unicode 是一个字符集,为世界上几乎所有的字符分配了唯一的编号(码点)。ord() 函数在 Python 中返回的就是字符对应的 Unicode 编码。
utf-8 是一种针对 Unicode 的可变长度字符编码,它将 Unicode 码点编码成 1 到 4 个字节的序列。例如,字符 '中' 的 Unicode 码点 20013 (十六进制为 0x4E2D),在 UTF-8 编码下会被编码为三个字节:0xE4B8AD,其十进制表示为 [228, 184, 189]。
以Unicode表示的str通过encode()方法可以编码为指定的bytes,可以通过以下代码查看 '中' 的 UTF-8 编码:
print('中'.encode('utf-8'))
# 这会输出类似 b'\xe4\xb8\xad' 的结果,b 前缀表示这是一个字节串,其中 \xe4\xb8\xad 就是 '中' 的 UTF - 8 编码表示。
print('ABC'.encode('ascii')) # b'ABC'
print('中文'.encode('utf-8')) # b'\xe4\xb8\xad\xe6\x96\x87'
▶ 运行结果:
b'\xe4\xb8\xad'
b'ABC'
b'\xe4\xb8\xad\xe6\x96\x87'
上面第三行print('中文'.encode('ascii'))会报错:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
编解码器无法对位置 0-1 的字符进行编码:序号不在范围 (128) 内
print(len('中')) # 1 一个字符
print(len('中'.encode('utf-8'))) # 3 utf8编码,中文占三个字节
print(len('中'.encode('gb2312'))) # 2 gb2312编码,中文占两个字节
▶ 运行结果:
1
3
2
s1 = "程序设计"
for char in s1:
print(
f"{char} 的 Unicode 编码是: {ord(char)},16进制形式为:\\u{ord(char):04x}")
▶ 运行结果:
程 的 Unicode 编码是: 31243,16进制形式为:\u7a0b
序 的 Unicode 编码是: 24207,16进制形式为:\u5e8f
设 的 Unicode 编码是: 35774,16进制形式为:\u8bbe
计 的 Unicode 编码是: 35745,16进制形式为:\u8ba1
字符串的运算
Python 语言为字符串类型提供了非常丰富的运算符,有很多运算符跟列表类型的运算符作用类似。例如,我们可以使用+运算符来实现字符串的拼接,可以使用*运算符来重复一个字符串的内容,可以使用in和not in来判断一个字符串是否包含另外一个字符串,我们也可以用[]和[:]运算符从字符串取出某个字符或某些字符。
拼接和重复
下面的例子演示了使用+和*运算符来实现字符串的拼接和重复操作。
s1 = 'hello' + ', ' + 'world'
print(s1) # hello, world
print('!' * 3) # !!!
▶ 运行结果:
hello, world
!!!
比较运算
对于两个字符串类型的变量,可以直接使用比较运算符来判断两个字符串的相等性或比较大小。需要说明的是,因为字符串在计算机内存中也是以二进制形式存在的,那么字符串的大小比较比的是每个字符对应的编码的大小。例如A的编码是65,而a的编码是97,所以'A' < 'a'的结果相当于就是65 < 97的结果,这里很显然是True;而'boy' < 'bad',因为第一个字符都是'b'比不出大小,所以实际比较的是第二个字符的大小,显然'o' < 'a'的结果是False,所以'boy' < 'bad'的结果是False。如果不清楚两个字符对应的编码到底是多少,可以使用ord函数来获得。下面的代码展示了字符串的比较运算,请大家仔细看看。
s1 = 'a whole new world'
s2 = 'hello world'
print(s1 == s2) # False
print(s1 < s2) # True
print(s1 == 'hello world') # False
print(s2 == 'hello world') # True
print(s2 != 'Hello world') # True
print('boy' < 'bad') # False
print('中' > '文') # False
print(ord('中')) # 20013
print(ord('文')) # 25991
▶ 运行结果:
False
True
False
True
True
False
False
20013
25991
成员运算
Python 中可以用in和not in判断一个字符串中是否包含另外一个字符或字符串,跟列表类型一样,in和not in称为成员运算符,会产生布尔值True或False,代码如下所示。
s1 = 'hello, world'
s2 = 'hello'
print('wo' in s1) # True
print('ll' not in s2) # False
print(s2 in s1) # True
s1 = "程序"
s2 = "Python 程序设计"
print(s1 in s2) # True
print("python" not in s1) # True
▶ 运行结果:
True
False
True
True
True
索引和切片
字符串的索引和切片操作跟列表、元组几乎没有区别,因为字符串也是一种有序序列,可以通过正向或反向的整数索引访问其中的元素。但是有一点需要注意,因为字符串是不可变类型,所以不能通过索引运算修改字符串中的字符。
s = 'abc123456'
n = len(s)
print(s[0], s[-n]) # a a
print(s[n - 1], s[-1]) # 6 6
print(s[2], s[-7]) # c c
print(s[5], s[-4]) # 3 3
print(s[2:5]) # c12
print(s[-7:-4]) # c12
print(s[2:]) # c123456
print(s[:2]) # ab
print(s[::2]) # ac246
print(s[::-1]) # 654321cba
▶ 运行结果:
a a
6 6
c c
3 3
c12
c12
c123456
ab
ac246
654321cba
字符的遍历
如果希望遍历字符串中的每个字符,可以使用for-in循环,有如下所示的两种方式。
# 方式一:
s = 'hello'
for i in range(len(s)):
print(s[i])
# 方式二:
s = 'hello'
for elem in s:
print(elem)
▶ 运行结果:
h
e
l
l
o
h
e
l
l
o
字符串的方法
在 Python 中,我们可以通过字符串类型自带的方法对字符串进行操作和处理,假设我们有名为foo的字符串,字符串有名为bar的方法,那么使用字符串方法的语法是:foo.bar(),这是一种通过对象引用调用对象方法的语法,跟前面使用列表方法的语法是一样的。
大小写相关操作
下面的代码演示了和字符串大小写变换相关的方法。
s1 = 'hello, world!'
# 字符串首字母大写
print(s1.capitalize()) # Hello, world!
# 字符串每个单词首字母大写
print(s1.title()) # Hello, World!
# 字符串变大写
print(s1.upper()) # HELLO, WORLD!
s2 = 'GOODBYE'
# 字符串变小写
print(s2.lower()) # goodbye
# 检查s1和s2的值
print(s1) # hello, world
print(s2) # GOODBYE
▶ 运行结果:
Hello, world!
Hello, World!
HELLO, WORLD!
goodbye
hello, world!
GOODBYE
说明:由于字符串是不可变类型,使用字符串的方法对字符串进行操作会产生新的字符串,但是原来变量的值并没有发生变化。所以上面的代码中,当我们最后检查s1和s2两个变量的值时,s1和s2 的值并没有发生变化。
查找操作
如果想在一个字符串中从前向后查找有没有另外一个字符串,可以使用字符串的find或index方法。在使用find和index方法时还可以通过方法的参数来指定查找的范围,也就是查找不必从索引为0的位置开始。
s = 'hello, world!'
print(s.find('or')) # 8
print(s.find('or', 9)) # -1 表示没有找到
print(s.find('of')) # -1
print(s.index('or')) # 8
# print(s.index('or', 9)) # ValueError: substring not found
▶ 运行结果:
8
-1
-1
8
说明:find方法找不到指定的字符串会返回-1,index方法找不到指定的字符串会引发ValueError错误。
find和index方法还有逆向查找(从后向前查找)的版本,分别是rfind和rindex,代码如下所示。
s = 'hello world!'
print(s.find('o')) # 4
print(s.rfind('o')) # 7
print(s.rindex('o')) # 7
# print(s.rindex('o', 8)) # ValueError: substring not found
▶ 运行结果:
4
7
7
性质判断
可以通过字符串的startswith、endswith来判断字符串是否以某个字符串开头和结尾;还可以用is开头的方法判断字符串的特征,这些方法都返回布尔值,代码如下所示。
s1 = 'hello, world!'
print(s1.startswith('He')) # False
print(s1.startswith('hel')) # True
print(s1.endswith('!')) # True
s2 = 'abc123456'
print(s2.isdigit()) # False
print(s2.isalpha()) # False
print(s2.isalnum()) # True
▶ 运行结果:
False
True
True
False
False
True
说明:上面的isdigit用来判断字符串是不是完全由数字构成的,isalpha用来判断字符串是不是完全由字母构成的,这里的字母指的是 Unicode 字符但不包含 Emoji 字符,isalnum用来判断字符串是不是由字母和数字构成的。
修剪操作
字符串的strip方法可以帮我们获得将原字符串修剪掉左右两端指定字符之后的字符串,默认是修剪空格字符。这个方法非常有实用价值,可以用来将用户输入时不小心键入的头尾空格等去掉,strip方法还有lstrip和rstrip两个版本,相信从名字大家已经猜出来这两个方法是做什么用的。
s1 = ' jackfrued@126.com '
print(s1.strip()) # jackfrued@126.com
s2 = '~你好,世界~'
print(s2.lstrip('~')) # 你好,世界~
print(s2.rstrip('~')) # ~你好,世界
▶ 运行结果:
jackfrued@126.com
你好,世界~
~你好,世界
替换操作
如果希望用新的内容替换字符串中指定的内容,可以使用replace方法,代码如下所示。replace方法的第一个参数是被替换的内容,第二个参数是替换后的内容,还可以通过第三个参数指定替换的次数。
s = 'hello, good world'
print(s.replace('o', '@')) # hell@, g@@d w@rld
print(s.replace('o', '@', 1)) # hell@, good world
▶ 运行结果:
hell@, g@@d w@rld
hell@, good world
拆分与合并
可以使用字符串的split方法将一个字符串拆分为多个字符串(放在一个列表中),也可以使用字符串的join方法将列表中的多个字符串连接成一个字符串,代码如下所示。
s = 'I love you'
words = s.split() # 将字符串按空格拆开成三个字符串'I','love','you'
print(words) # ['I', 'love', 'you']
print('~'.join(words)) # I~love~you
▶ 运行结果:
['I', 'love', 'you']
I~love~you
需要说明的是,split方法默认使用空格进行拆分,我们也可以指定其他的字符来拆分字符串,而且还可以指定最大拆分次数来控制拆分的效果,代码如下所示。
s = 'I#love#you#so#much'
words = s.split('#')
print(words) # ['I', 'love', 'you', 'so', 'much']
words = s.split('#', 2)
print(words) # ['I', 'love', 'you#so#much']
▶ 运行结果:
['I', 'love', 'you', 'so', 'much']
['I', 'love', 'you#so#much']
split方法的一个常用的用法,就是使用一条input语句,输入多个变量的值
x, y, z = input("请输入三个值,以逗号分隔: ").split(',')
print(f"x的值为: {x}, y的值为: {y}, z的值为: {z}")
print(type(x), type(y), type(z))
▶ 运行结果:
x的值为: 1, y的值为: 2, z的值为: 3
<class 'str'> <class 'str'> <class 'str'>
这里不能用int()转换三个变量的类型,常用map函数将输入的值转换为需要的类型
x, y, z = map(int, input("请输入三个值,以逗号分隔: ").split(','))
s = x + y + z
print(f"和为: {s}")
▶ 运行结果:
和为: 6
去除空白与判断
str.strip(chars=None):去除首尾指定字符(默认去除空格、换行符\n、制表符\t等)
(扩展:lstrip()仅去左侧,rstrip()仅去右侧)
s = " hello a \n a "
print(s.strip())
print("###hello###a".strip("#"))
▶ 运行结果:
hello a
a
hello###a
判断类方法(返回布尔值):
str.startswith(prefix):是否以prefix开头
str.endswith(suffix):是否以suffix结尾
str.isdigit():是否全为数字(0-9)
str.isalpha():是否全为字母(a-z, A-Z)
str.isspace():是否全为空白字符
s = "Python123"
print(s.startswith("Py")) # True
print(s.endswith("3")) # True
print(s.isdigit()) # False(含字母)
▶ 运行结果:
True
True
False