字符串是一个由字符数据组成的序列。字符串处理是编程必备的技能,很少有应用程序不需要操作字符串的。
Python 提供了丰富的运算符、函数和类方法来操作字符串。
通过本文,你将了解如何访问字符串以及提取字符串的子串,并熟悉用于操作和修改字符串数据的方法。
我们也会向你介绍 Python 中另外两种表示裸字节数据的对象:bytes 和 bytearray。
【字符串操作】
下边,我们将着重介绍用于字符串操作的运算符、类方法和函数。
1,字符串运算符
在《Python 运算符和表达式》一文中,我们已经在算术运算中使用过 + 和 * 运算符。这两个运算符不仅适用于数字操作数,它们也可用于字符串。
+ 运算符用于连接字符串,它返回一个由所有操作数连接在一起的字符串。
>>> s = 'foo' >>> t = 'bar' >>> u = 'baz' >>> >>> s + t 'foobar' >>> s + t + u 'foobarbaz' >>> >>> print('Go team' + '!!!') Go team!!!
* 运算符用于创建一个字符串的多份拷贝,并将这些拷贝连接起来。
若 s 是一个字符串,n 是一个整数,下边两个表达式均会返回连接起来的字符串 s 的 n 份拷贝。
s * n n * s
例如:
>>> s = 'foo.' >>> s * 4 '; >>> 4 * s ';
这里,n 必须为一个整数。你可能会觉得 n 还得必须为正整数,事实是,它可以是 0 或者 负数,这时候计算结果为空字符串。
>>> 'foo' * -8 ''
Python 提供了一个可用于字符串的成员关系运算符:in。
若 in 运算符的左操作数包含于右操作数中,in 返回 True,否则返回 False。
>>> s = 'foo' >>> s in "That's food for thought" True >>> s in "That's good for now" False
Python 还提供了 not in 运算符,含义正好和 in 相反。
>>> 'z' not in 'abc' True >>> 'z' not in 'xyz' False
2,内置字符串函数
Python 提供了很多内置函数,可用于字符串操作。比如:
函数 | 描述 |
chr() | 将整数转化为字符 |
ord() | 将字符转化为整数 |
len() | 返回字符串的长度 |
str() | 返回一个对象的字符串表示 |
我们逐一了解一下这几个函数。
ord(c):返回字符的整数值
从最基本层面来讲,计算机是以数字的形式存储所有信息的。为了表示字符数据,我们可以用一个模式来翻译字符和对应数字之间的映射关系。
通常使用的最简单的模式就是 ASCII。它覆盖了我们最习惯使用的常见的拉丁字符。对于这些字符,ord(c) 返回字符 c 的 ASCII 值。
>>> ord('a') 97 >>> ord('#') 35
从本教程开始到现在,ASCII 很好地满足了我们的需求。但世界上还存在很多其他语言,在数字媒体领域也包含无数的符号。因此,需要用计算机编码表示的完整的字符集规模远超普通拉丁字符、数字和常见符号。
Unicode 编码标准在这方面雄心勃勃,它试图提供一个覆盖所有字符、所有语言、所有平台的数字编码集。
Python 3 对 Unicode 提供了广泛的支持,允许你在字符串中使用 Unicode 字符。
只要你使用范围限于普通字符,ASCII 和 Unicode 并无实操上的区别。而对于 Unicode 字符,ord(c) 函数也会返回其数字值。
>>> ord('€') 8364 >>> ord('∑') 8721
chr(n):返回整数的字符值
chr() 和 ord() 用途恰好相反。对于一个给定的数字,chr(n) 返回一个字符串,其值为和 n 对应的字符。
>>> chr(97) 'a' >>> chr(35) '#'
chr() 也可处理 Unicode 字符。
>>> chr(8364) '€' >>> chr(8721) '∑'
len(s):返回字符串的长度
你可以使用 len() 来检查字符串的长度,len(s) 返回字符串 s 中 字符的个数。
>>> s = 'I am a string.' >>> len(s) 14
str(obj):返回一个对象的字符串表示
Python 中的每个对象都可使用字符串来表示,str(obj) 返回对象 obj 的字符串表示。
>>> str) '39.2' >>> str(3+4j) '(3+4j)' >>> str(3 + 29) '32' >>> str('foo') 'foo'
3,字符串索引
在编程语言里,有序数据集中的单个元素通常可以直接通过数字索引或键值来访问。这个过程就是指索引(indexing)。
Python 中的字符串是字符数据的有序序列,因此可以用这种方式建立索引。字符串中的单个字符可通过字符串名称加方括号([])的方式来访问,方括号中是一个数字,即字符在字符串中的位置。
Python 中字符串索引以 0 开始。字符串中第一个字符的索引为 0,下一个索引为 1,以此类推。最后一个字符的索引为字符串长度减 1.
比如:字符串 'foobar' 的索引简图可表示为:
每个字符可通过如下方式来访问:
>>> s = 'foobar' >>> s[0] 'f' >>> s[1] 'o' >>> s[3] 'b' >>> len(s) 6 >>> s[len(s) - 1] 'r'
索引范围超出字符串尾部,将会导致错误:
>>> s[6] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: string index out of range
这种情况称为“访问越界”。
字符串索引值也可以为负数,这种情况下索引就是自字符串尾部倒着进行:-1 指向最后一个字符,-2 指向倒数第二个字符,以此类推。
下图展示了在字符串 'foobar' 上同时使用正值索引和负值索引的情况:
看一些负值索引的例子:
>>> s = 'foobar' >>> s[-1] 'r' >>> s[-2] 'a' >>> len(s) 6 >>> s[-len(s)] 'f'
负值索引若超出字符串首部也会报错:
>>> s[-7) File "<stdin>", line 1 s[-7) ^ SyntaxError: invalid syntax
对于任何非空字符串,s[len(s)-1] 和 s[-1] 均返回最后一个字符。
对空字符串进行索引访问没有任何意义。
4,字符串切片
字符串切片是 Python 提供的一种索引语法格式,用于从字符串中提取子串。
若 s 是一个字符串,表达式 s[m:n] 返回一个 s 的子字符串,该字符串起始于 s 的索引为 m 的位置,终止于但不含索引为 n 的位置。
>>> s = 'foobar' >>> s[2:5] 'oba'
这个例子中,第二个索引(5)指向了字符串 'foobar' 的第一个不包含在子串中的字符 'r'(s[5])。
这似乎和直观认识有点不符,但这种表示方法有其有意义的地方:表达式 s[m:n] 返回了一个长度为 n-m 的子串,你能一眼就看出子串的长度。上边例子里,子串的长度就是 5-2=3.
表达式 s[m:n] 的第一个索引 m 可以被省略,省略后切片就从字符串 s 的第一个字符开始,即 s[:n] 等同于 s[0:n]。
>>> s = 'foobar' >>> s[:4] 'foob' >>> s[0:4] 'foob'
与之类似,第二个索引 n 也可以被省略,省略后切片会自动从第一个索引位置扩展到字符串结尾。s[m:] 和 s[m:len(s)] 效果相同,但显然比后者更友好简洁。
>>> s = 'foobar' >>> s[2:] 'obar' >>> s[2:len(s)] 'obar'
对于任何一个字符串 s 和 任何整数 n( 0≤n≤len(s) ),s[:n] + s[n:] == s。
>>> s = 'foobar' >>> t = s[:] >>> t 'foobar' >>> id(s) 1933478254320 >>> id(t) 1933478254320 >>> t is s True
若省略 s[m:n] 的两个索引,即 s[:],表达式将会返回原来的字符串。这个返回值并非原字符串的拷贝,它是对原字符串的引用。
>>> s[2:2] '' >>> s[4:2] ''
若切片表达式的第一个索引大于等于第二个索引,Python 将返回一个空字符串。
>>> s[2:2] '' >>> s[4:2] ''
和访问字符串中的单个字符一样,负值索引也可以应用到字符串切片表达式中。
下图展示了如何从字符串 ‘foobar’ 中提取子串 'oob',图中分别标注了使用正值索引和负值索引两种情形。
看一下相应的 Python 代码:
>>> s = 'foobar' >>> s[-5:-2] 'oob' >>> s[1:4] 'oob' >>> s[-5:-2] == s[1:4] True
5,指定切片步长
切片表达式的语法还有一种情况需要探讨,就是,再增加一个冒号和一个参数 step,即:s[m:n:step]。step 为步长,表示每获取一个字符后,在字符串中行进的距离。
比如,对于字符串 'foobar',切片 0:6:2 表示从第一个字符开始,每隔一个字符取下一个字符,直到最后一个字符。可用下图表示:
用代码表示为:
>>> s = 'foobar' >>> s[0:6:2] 'foa'
你同样可以省略表达式中前两个索引,Python 解释器会默认取第一个和最后一个字符:
>>> s = '12345' * 5 >>> s '1234512345123451234512345' >>> s[::5] '11111' >>> s[4::5] '55555'
步长可以为负值,这种情况下,Python 会倒序遍历字符串,此时要求第一个(起始)索引值要大于第二个(结束)索引值。
>>> s = 'foobar' >>> s[5:0:-2] #从最后一个字符开始倒序每隔一个字符取下一个字符直到(不含)首字符 'rbo'
当倒序步进访问字符串时,若省略前两个索引参数,这两个参数的默认取值将分别是末字符和首字符的索引。比如:
>>> s = '12345' * 5 >>> s '1234512345123451234512345' >>> s[::5] '11111' >>> s[::-5] '55555'
这可用于翻转一个字符串:
>>> s = 'If Comrade Napoleon says it, it must be right.' >>> s[::-1] '.thgir eb tsum ti ,ti syas noelopaN edarmoC fI'
6,将变量插入到字符串中
Python 3.6 引入了一种新的字符串格式化机制。该机制名为“格式化的字符串字面量”,通常也叫做 f-string。
f-string 拥有很多特性,我们会在后续文章中进行专题介绍,这里不作详细展开。
不过,你现在可以使用一个 f-string 的简单特性:变量插值。
f-string 字面量允许你在其中直接指定变量名称,Python 解释 f-string 形式的字符串时,会用变量的值来替代变量名称。
假设你要显示一个算术运算的结果,你可以直接通过 print() 语句来输出,在 print() 中使用逗号来分隔数字值和字符串常量。
>>> n = 20 >>> m = 25 >>> prod = n * m >>> print('The product of', n, 'and', m, 'is', prod) The product of 20 and 25 is 500
这种写法有些笨拙。我们可以使用 f-string 来实现同样的目的:
- 在字符串常量的上引号前添加一个大写 F 或小写 f,指示 Python 解释器这是一个 f-string,而非一个普通字符串
- 使用大括号({})在 f-string 中插入任意变量
使用 f-string 来转换上边那段代码,会显得更清晰些:
>>> n = 20 >>> m = 25 >>> prod = n * m >>> print(f'The prod of {n} and {m} is {prod}') The prod of 20 and 25 is 500
定义 Python 字符串的三种形式的引号都可以用来定义 f-string:
>>> var = 'Bark' >>> print(f'A dog says {var}') A dog says Bark >>> print(f"A dog says {var}") A dog says Bark >>> print(f'''A dog says {var}''') A dog says Bark
7,修改字符串
简单说,我们无法修改一个字符串。字符串是 Python 中的一种不可变数据类型。
事实上,我们目前接触到的这些数据类型都是不可变的。当然,Python 也提供了其他可变数据类型,我们很快就会学习这些类型。
如果你尝试修改字符串的内容,你会得到如下报错:
>>> s = 'foobar' >>> s[3] = 'x' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object does not support item assignment
而实际中,你也没必要非得修改字符串。你可以轻松生成字符串的一份拷贝,并在拷贝过程中完成字符串内容的修改。
Python 中有很多方法可以完成这项功能。下边是一种可能的方法:
>>> s = s[:3] + 'x' + s[4:] >>> s 'fooxar'
这也可以通过一个内置的字符串类方法来实现:
>>> s = 'foobar' >>> s = s.replace('b', 'x') >>> s 'fooxar'
我们接着来了解这些内置的字符串类方法。
8,内置字符串类方法
我们知道,Python 是一个高度面向对象的语言,Python 程序中的每个数据元素都是一个对象。
你也熟悉了 Python 中的函数,它们是可供调用的用于执行特定任务的过程。
方法和函数近似。它是一个特定类型的可调用过程,和对象紧密相连。
方法也用于执行特定的任务,但方法只能通过对象来调用,它在执行过程中对目标对象了如指掌。
调用作用于对象的方法的语法为:
obj.foo(<args>)
这将在 对象 obj 上调用 .foo() 方法,<args> 为传递给该方法的参数,如果有的话。
我们会在后续讨论面向对象编程时学习更多定义和调用类方法的知识。此处,我们仅了解一些常用的字符串操作相关的方法。
下边按用途分别介绍这些字符串类方法,其中,中括号中的参数表明这些参数是可选的。
8.1,大小写转换
s.capitalize():将目标字符串的首字母转为大写字符,其他字符转为小写字符,非字母字符保持不变。
>>> s = 'foO BaR BAZ quX' >>> s.capitalize() 'Foo bar baz qux' >>> >>> s = 'foo123#BAR#.' >>> s.capitalize() 'Foo123#bar#.'
s.lower():将字母字符转为小写
>>> 'FOO Bar 123 baz qUX'.lower() 'foo bar 123 baz qux'
s.swapcase():切换字母字符的大小写
>>> 'FOO Bar 123 baz qUX'.swapcase() 'foo bAR 123 BAZ Qux'
s.title():将字符串中每个单词的首字母转为大写,其他字母转为小写
>>> 'the sun also rises'.title() 'The Sun Also Rises'
此方法使用的是很简单的算法,不区分单词的重要性,也没有妥善处理一些符号。
>>> "what's happened to ted's IBM stock?".title() "What'S Happened To Ted'S Ibm Stock?"
s.upper():将字母字符转为大写
>>> 'FOO Bar 123 baz qUX'.upper() 'FOO BAR 123 BAZ QUX'
8.2,查找与替换
本组中的类方法提供了各种在目标字符串中搜索子串的方法。每个类方法都支持可选的 <start> 和 <end> 参数,这两个参数用作字符串切片:方法仅在 [start, end) 范围内对目标字符串执行搜索。如果指定了 <start> 而没有指定 <end>,搜索范围就是自 start 指向的位置直到字符串结尾。
s.count(<sub> [, <start>[, <end>]]):计算子串非重叠出现的次数
>>> 'foo goo moo'.count('oo') 3 >>> 'foo goo moo'.count('oo', 0, 8) 2
s.endswith(<suffix>[, <start>[, <end>]]):判断是否以给定的子串结尾
>>> 'foobar'.endswith('bar') True >>> 'foobar'.endswith('baz') False >>> 'foobar'.endswith('oob', 0, 4) True >>> 'foobar'.endswith('oob', 2, 4) False
s.find(<sub>[, <start>[, <end>]]):查找子串。若找到,返回子串的最小索引值;否则,返回 -1
>>> 'foo bar foo baz foo qux'.find('foo') 0 >>> 'foo bar foo baz foo qux'.find('grault') -1 >>> 'foo bar foo baz foo qux'.find('foo', 4) 8 >>> 'foo bar foo baz foo qux'.find('foo', 4, 7) -1
s.index(<sub>[, <start>[, <end>]]):同 s.find(),若未找到抛异常
>>> 'foo bar foo baz foo qux'.index('grault') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: substring not found
s.rfind(<sub>[, <start>[, <end>]]):从尾端开始查找子串。若找到,返回子串的最大索引值;否则,返回 -1
>>> 'foo bar foo baz foo qux'.rfind('foo') 16 >>> 'foo bar foo baz foo qux'.rfind('grault') -1 >>> 'foo bar foo baz foo qux'.rfind('foo', 0, 14) 8 >>> 'foo bar foo baz foo qux'.rfind('foo', 10, 14) -1
s.rindex(<sub>[, <start>[, <end>]]):同 s.rfind(),若未找到抛异常
>>> 'foo bar foo baz foo qux'.rindex('grault') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: substring not found
s.startswith(<prefix>[, <start>[, <end>]]):判断是否以给定的子串开头
>>> 'foobar'.startswith('foo') True >>> 'foobar'.startswith('bar') False >>> 'foobar'.startswith('bar', 3) True >>> 'foobar'.startswith('bar', 3, 2) False
8.3,字符分类
s.isalnum():判断字符串是否完全由字母或数字组成
>>> 'abc123'.isalnum() True >>> 'abc$123'.isalnum() False >>> ''.isalnum() False
s.isalpha():判断字符串是否完全由字母组成
>>> 'ABCabc'.isalpha() True >>> 'abc123'.isalpha() False
s.isdigit():判断字符串是否完全由数字组成
>>> '123'.isdigit() True >>> '123abc'.isdigit() False
s.isidentifier():判断字符串是否是一个合法的 Python 标识符,即是否符合 Python 标识符命名规范。
>>> 'foo32'.isidentifier() True >>> '32foo'.isidentifier() False >>> 'foo$32'.isidentifier() False
若字符串为 Python 关键字,尽管不是合法标识符,此函数亦返回 True:
>>> 'and'.isidentifier() True
若你想测试字符串是否为 Python 关键字,可调用 keyword 模块中的 iskeyword() 函数:
>>> from keyword import iskeyword >>> iskeyword('and') True
这样,可结合 s.isidentifier() 和 keyword.iskeyword() 来判断字符串是否为一个真正合法的标识符:
>>> 'and'.isidentifier() and not iskeyword('and') False >>> 'apple'.isidentifier() and not iskeyword('apple') True
s.islower():判断字符串中的字母字符是否全为小写字母
>>> 'abc'.islower() True >>> 'abc1$d'.islower() True >>> 'Abc1$D'.islower() False
s.isprintable():判断字符串中的字母字符是否全为可打印字符
>>> 'a\tb'.isprintable() False >>> 'a b'.isprintable() True >>> ''.isprintable() True >>> 'a\nb'.isprintable() False
s.isspace():判断字符串中的字符是否全为空白字符,常见的空白字符为' '、'\t' 和 '\n'
>>> ' \t \n '.isspace() True >>> ' a '.isspace() False
s.istitle():判断字符串是否为 title 形式的大小写,参上文 s.title()
>>> 'This Is A Title'.istitle() True >>> 'This is a title'.istitle() False >>> 'Give Me The #$#@ Ball!'.istitle() True
s.isupper():判断字符串中的字母字符是否全为大写字母
>>> 'ABC'.isupper() True >>> 'ABC1$D'.isupper() True >>> 'Abc1$D'.isupper() False
8.4,字符串格式化
s.center(<width>[, <fill>]):将字符串居中到一个指定宽度的字段,可设置填充字符,默认使用空格填充
>>> 'foo'.center(10) ' foo ' >>> 'bar'.center(10, '-') '---bar----'
若 s 长度超过设置的宽度,s 保持不变:
>>> 'foo'.center(2) 'foo'
s.expandtabs(tabsize=8):替换字符串中的 tab 字符为指定长度的空格符
>>> 'aaa\tbbb\tc'.expandtabs() 'aaa bbb c' >>> 'a\tb\tc'.expandtabs(4) 'a b c'
s.ljust(<width>[, <fill>]):将字符串左对齐到一个指定宽度的字段,可设置填充字符,默认使用空格填充
>>> 'foo'.ljust(10) 'foo ' >>> 'foo'.ljust(10, '-') 'foo-------' >>> 'foo'.ljust(2) 'foo'
s.lstrip([<chars>]):移除字符串左侧指定的字符,默认移除空白字符
>>> ' foo bar baz '.lstrip() 'foo bar baz ' >>> '\t\nfoo\t\nbar\t\nbaz'.lstrip() 'foo\t\nbar\t\nbaz' >>> ';.lstrip('/:pth') 'www.real;
s.replace(<old>, <new>[, <count>]):将字符串中<old>指定的子串替换为<new>,可指定替换次数,默认全部替换
>>> 'foo bar foo baz foo qux'.replace('foo', 'grault') 'grault bar grault baz grault qux' >>> 'foo bar foo baz foo qux'.replace('foo', 'grault', 2) 'grault bar grault baz foo qux'
s.rjust(<width>[, <fill>]):将字符串左对齐到一个指定宽度的字段,可设置填充字符,默认使用空格填充
>>> 'foo'.rjust(10) ' foo' >>> 'foo'.rjust(10, '-') '-------foo' >>> 'foo'.rjust(2) 'foo'
s.rstrip([<chars>]):移除字符串右侧指定的字符,默认移除空白字符
>>> ' foo bar baz '.rstrip() ' foo bar baz' >>> 'foo\t\nbar\t\nbaz\t\n'.rstrip() 'foo\t\nbar\t\nbaz' >>> 'foo.$$$;'.rstrip(';$.') 'foo'
s.strip([<chars>]):移除字符串中指定的字符,默认移除空白字符。相当于连续使用 s.lstrip() 和 s.rstrip()
>>> s = ' foo bar baz\t\t\t' >>> s = s.lstrip() >>> s = s.rstrip() >>> s 'foo bar baz' >>> ' foo bar baz\t\t\t'.strip() 'foo bar baz'
s.zfill(<width>):在字符串左侧填充字符'0',以使得字符串达到 width 指定的长度
>>> '42'.zfill(5) '00042' >>> '-42'.zfill(3) '-42'
若 s 包含一个引导标记,此标记仍保留在填充字符串的左侧
>>> '+42'.zfill(8) '+0000042' >>> '-42'.zfill(8) '-0000042'
此函数非常适合处理数字字符串,但也可以处理非数字字符串:
>>> 'foo'.zfill(6) '000foo'
8.5,字符串和 list 间进行转换
本组类方法可将 Python 中的某些复合数据类型对象“粘贴”在一起形成一个字符串,也可以将字符串打散为零碎对象的集合。
这些方法操作或返回可迭代对象,可迭代对象是 Python 中用于描述顺序对象集合的通用术语。我们马上就会学习这些对象。现在你只需将它们看做一个包含很多值的序列就行了。
list 对象定义在中括号中,tuple 对象定义在圆括号中。
有了这些知识,我们就可以开始学习最后一组字符串类方法了。
s.join(<iterable>):返回一个将可迭代对象中的元素连接起来的字符串,并以 s 作为元素间的分隔符。
由于 join() 作用在字符串 s 之上,<iterable> 也必须是一个包含字符串对象的序列。
看个例子:
>>> ', '.join(['foo', 'bar', 'baz', 'qux']) 'foo, bar, baz, qux'
这个例子中,分隔符是 ', ',<iterable> 是包含字符串值的 list。
再看一个例子。
>>> list('corge') ['c', 'o', 'r', 'g', 'e'] >>> >>> ':'.join('corge') 'c:o:r:g:e'
这个例子中,<iterable> 是一个单一的字符串 'corge'。当字符串作为 iterable 来解释时,它被视为一个由字符串里的字符组成的 list。因此,':'.join('corge') 的结果就是使用冒号将 'corge' 中的字符连接成一个新的字符串。
如果 <iterable> 中的对象不是字符串,s.join() 就会失败:
>>> '---'.join(['foo', 23, 'bar']) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sequence item 1: expected str instance, int found
可做如下修改:
>>> '---'.join(['foo', str(23), 'bar']) 'foo---23---bar'
Python 中很多复合类型的对象都可以构建为 iterable,当需要从这些对象创建字符串时,s.join() 会很有用处。
s.partition(<sep>):基于字符串 sep 来分隔字符串 s。
此方法以 s 中的第一个 sep 为分隔点,将字符串 s 分隔为三部分,并保存在一个 tuple 中:(<sep>之前的部分, <sep>, <sep>之后的部分)。
>>> ';.partition('.') ('foo', '.', 'bar') >>> 'foo@@bar@@baz'.partition('@@') ('foo', '@@', 'bar@@baz')
若 s 中不含 <sep>,返回的 tuple 为:(s, '', ''):
>>> ';.partition('@@') (';, '', '')
s.rpartition(<sep>):和 s.partition(<sep>) 类似,不过是从字符串 s 的右侧开始查找 <sep>。
>>> 'foo@@bar@@baz'.partition('@@') ('foo', '@@', 'bar@@baz') >>> 'foo@@bar@@baz'.rpartition('@@') ('foo@@bar', '@@', 'baz')
s.rsplit(sep=None, maxsplit=-1):切分字符串 s,返回一个包含子串的 list。
参数为空时,此方法使用空白字符来切割字符串。
>>> 'foo bar baz qux'.rsplit() ['foo', 'bar', 'baz', 'qux'] >>> 'foo\n\tbar baz\r\f qux'.rsplit() ['foo', 'bar', 'baz', 'qux']
若指定了<sep>参数,<sep> 将被用作切割点。
>>> ';.rsplit(sep='.') ['foo', 'bar', 'baz', 'qux']
若 <sep> 的值为 None,其效果相当于并没有指定 <sep> 参数。
若明确指定了 <sep> 作为分隔符,字符串 s 中连续的分隔符会被认为切割空字符串,并返回切割的空字符串。
>>> 'foo...bar'.rsplit(sep='.') ['foo', '', '', 'bar']
若省略 <sep> 参数,即默认使用空白字符作为分隔符,字符串 s 中连续的空白字符会被视为一个字符处理,且结果中也不会出现空字符串。这不同于指定 <sep> 值的情况。
>>> 'foo\t\t\tbar'.rsplit() ['foo', 'bar']
若指定了关键字参数 <maxsplit> 的值,此方法最多从字符串 s 的右端开始执行 maxsplit 次分割。
>>> 'www.real;.rsplit(sep='.', maxsplit=1) ['www.realpython', 'cn']
若为指定,<maxsplit> 默认值为 -1,会执行所有可能的分割。
>>> 'www.real;.rsplit(sep='.', maxsplit=-1) ['www', 'realpython', 'cn'] >>> 'www.real;.rsplit(sep='.') ['www', 'realpython', 'cn']
s.split(sep=None, maxsplit=-1):和 s.split() 类似,区别是,若指定了 <maxsplit> 参数,此方法最多从字符串 s 的左端开始执行 maxsplit 次分割。
>>> 'www.real;.split('.', maxsplit=1) ['www', 'real;] >>> 'www.real;.rsplit('.', maxsplit=1) ['www.realpython', 'cn']
若不指定 <maxsplit>,s.split() 和 s.rsplit() 无区别。
s.splitlines([<keepends>]):按行切割字符串,返回一个包含子串的 list。
被视为行边界的字符或字符串有:\n、\r、\r\n、\v或\x0b、\f或\x0c、\x1c、\x1d、\x1e、\x85、\u2028、\u2029。
看个例子:
>>> 'foo\nbar\r\nbaz\fqux\u2028quux'.splitlines() ['foo', 'bar', 'baz', 'qux', 'quux']
字符串中连续的行边界字符会被视为空行,这些空行也会出现在结果 list 中。
>>> 'foo\f\f\fbar'.splitlines() ['foo', '', '', 'bar']
若指定了 <keepends> 且为真值,行边界字符也会保留在结果子串中。
>>> 'foo\nbar\nbaz\nqux'.splitlines(True) ['foo\n', 'bar\n', 'baz\n', 'qux'] >>> 'foo\nbar\nbaz\nqux'.splitlines(1) ['foo\n', 'bar\n', 'baz\n', 'qux']
【bytes 对象】
bytes 对象是 Python 内置的一种操作二进制数据的数据类型。
一个 bytes 对象是一个单字节值组成的序列,是不可变的,其中的每一个元素都是一个 0-255 范围内的小整数。
1,定义一个字面 bytes 对象
bytes 字面量(常量)的定义方法和字符串字面量定义方法相同,只是多了一个 'b' 前缀。
>>> b = b'foo bar baz' >>> b b'foo bar baz' >>> type(b) <class 'bytes'>
同样,可以使用单引号、双引号和三引号三种方法来定义 bytes 字面量。
>>> b'Contains embedded "double" quotes' b'Contains embedded "double" quotes' >>> b"Contains embedded 'single' quotes" b"Contains embedded 'single' quotes" >>> b'''Contains embedded "double" and 'single' quotes''' b'Contains embedded "double" and \'single\' quotes' >>> b"""Contains embedded "double" and 'single' quotes""" b'Contains embedded "double" and \'single\' quotes'
bytes 字面量仅允许使用 ASCII 字符,值大于 127 的字符需要进行转义处理:
>>> b = b'foo\xddbar' >>> b b'foo\xddbar' >>> b[3] 221 >>> int(0xdd) 221
bytes 字面量也可以使用 ‘r’ 前缀,这样就禁用了转义语义。
>>> b = rb'foo\xddbar' >>> b b'foo\\xddbar' >>> b[3] 92 >>> chr(92) '\\'
2,使用内置 bytes() 函数定义 bytes 对象
bytes() 函数也可以用来创建一个 bytes 对象,该函数可接受多种参数组合,从而返回不同类型的 bytes 对象。
下面一起了解一下这些不同类型的参数形式。
bytes(<s>, <encoding>):从一个字符串构建 bytes 对象。<encoding> 也是一个必需参数,它指定了将字符串中的字符解释成整数值的方式。
>>> b = bytes(';, 'utf8') >>> b b'; >>> type(b) <class 'bytes'>
bytes(<size>):构建一个由 null(0x00)字节组成的长度为<size>的 bytes 对象。
>>> b = bytes(8) >>> b b'\x00\x00\x00\x00\x00\x00\x00\x00' >>> type(b) <class 'bytes'>
bytes(<iterable>):从一个可迭代对象构建一个 bytes 对象。<iterable> 必须是一个包含整数值的序列,序列中整数的取值范围为 0 - 255.
>>> b = bytes([100, 102, 104, 106, 108]) >>> b b'dfhjl' >>> type(b) <class 'bytes'> >>> b[2] 104
3,操作 bytes 对象
和字符串一样,bytes 对象支持常见的序列操作:
- in 和 not in 运算符>>> b = b'abcde'
>>> b'cd' in b
True
>>> b'foo' not in b
True - 连接(+)和复制(*)运算符>>> b = b'abcde'
>>> b + b'fghi'
b'abcdefghi'
>>> b * 3
b'abcdeabcdeabcde' - 索引和切片>>> b = b'abcde'
>>> b[2]
99
>>> b[1:3]
b'bc' - 内置函数>>> b = b'abcde'
>>> len(b)
5
>>> min(b)
97
>>> max(b)
101
许多为字符串对象定义的方法也适用于 bytes 对象:
>>> b = b'foo,bar,foo,baz,foo,qux' >>> b.count(b'foo') 3 >>> b.endswith(b'qux') True >>> b.find(b'baz') 12 >>> b.split(sep=b',') [b'foo', b'bar', b'foo', b'baz', b'foo', b'qux'] >>> b.center(30, b'-') b'---foo,bar,foo,baz,foo,qux----'
需要注意,当在 bytes 对象上调用这些运算符和方法时,使用的操作数和参数必须是 bytes 对象。
>>> b = b'; >>> b + '.baz' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't concat str to bytes >>> b + b'.baz' b'; >>> >>> b.split(sep='.') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: a bytes-like object is required, not 'str' >>> b.split(sep=b'.') [b'foo', b'bar']
尽管 bytes 对象的定义和表示是基于 ASCII 文本的,它实际上表现为一个由[0, 255]范围内的小整数组成的不可变序列。这也是 bytes 对象中的单一元素会显示为整数的原因。
>>> b = b'foo\xddbar' >>> b[3] 221 >>> hex(b[3]) '0xdd' >>> min(b) 97 >>> max(b) 221
而 bytes 对象的一个切片则仍会显示为一个 bytes 对象,即使这个切片只有一个 byte 长。
>>> b = b'abcde' >>> b[2:3] b'c'
可使用内置的 list() 函数将一个 bytes 对象转换为一个包含整数的 list。
>>> b = b'abcde' >>> list(b) [97, 98, 99, 100, 101]
十六进制数字常被用来表示二进制数据,因为两个十六进制数字刚好对应一个字节。bytes 类提供了两个额外的方法用于和十六进制字符串之间进行互转操作。
by(<s>):由一个十六进制值字符串构建一个 bytes 对象。
此方法将字符串 s 中的每对十六进制形式的字符转换为一个 byte 值,s 中的十六进制数字对之间可包含空格,这些空格会被忽略。
>>> b = by(' aa 68 4682cc ') >>> b b'\xaahF\x82\xcc' >>> list(b) [170, 104, 70, 130, 204]
注意,此方法是一个类方法,需要通过 bytes 类来调用。我们会在后续了解何为类方法。
b.hex():返回一个 bytes 对象的十六进制表示的字符串。
和 by() 不同,这是一个对象方法。
>>> b = by(' aa 68 4682cc ') >>> b b'\xaahF\x82\xcc' >>> list(b) [170, 104, 70, 130, 204] >>> >>> b.hex() 'aa684682cc' >>> type()) <class 'str'>
4,bytearray 对象
bytearray 是 Python 支持的另一种二进制序列数据类型。它和 bytes 很相似,区别在于:
- bytearray 只能通过内置函数 bytearray() 来创建,它没有类似 b'' 这样的用于定义 bytes 字面量的语法。>>> ba = bytearray(';, 'UTF-8')
>>> ba
bytearray(b';)
>>>
>>> bytearray(6)
bytearray(b'\x00\x00\x00\x00\x00\x00')
>>>
>>> bytearray([100, 102, 104, 106, 108])
bytearray(b'dfhjl') - bytearray 对象是可变的,可通过索引和切片修改其内容。>>> ba = bytearray(';, 'UTF-8')
>>> ba
bytearray(b';)
>>>
>>> ba[5] = 0xee
>>> ba
bytearray(b'\xeer.baz')
>>>
>>> ba[8:11] = b'qux'
>>> ba
bytearray(b'\xeer.qux')
bytearray 对象也可由 bytes 对象直接创建。
>>> ba = bytearray(b'foo') >>> ba bytearray(b'foo')
【结语】
本文较深入地介绍了 Python 提供的多种字符串操作机制,包括字符串运算符、内置函数、索引、切片和类方法。同时也介绍了 bytes 和 bytearray 两种二进制数据类型。
这是我们第一次使用 Python 中的复合数据类型:它们均由更小的数据组成。
Python 提供了多种内置的复合数据类型,我们会在下一次教程文章中探索两个最常使用的类型:list 和 tuple。
敬请关注!