【Python学习笔记】 第7章 字符串基础
本章范围本章主要讲str字符串类型,有关的操作适用于Unicode处理。
Unicode简介
ASCII是Unicode的简单形式,但Unicode适用于非英语地区的人们。两者在文件中的编码不同。
[*]在Python 3.X中,有三种字符串类型:str用于Unicode文本,bytes用于二进制数据,bytearray是bytes的一种可修改的变体。
[*]在Python 2.X中,unicode字符串表示Unicode文本,str同时处理8位文本和二进制数据。
实际上,Unicode的主要不同在于它在内存和文件之间来回移动所要求的转换步骤。除此之外它大体上只是一个字符串处理过程。
字符串基础
字符串可以用来表示能够编码为文本或字节的人和事物。Python的字符串有一套强大的处理工具集,并划分为不可变序列的类型(即不能在原位置修改字符串)。
以下是常见的字符串字面量和操作:
操作解释S = ''空字符串S = "spam's"双引号,和单引号相同S = 's\np\ta\x00m包含转义序列S = """...multiline..."""三引号块字符串S = r'\temp\spam'原始字符串,不转义B = b'sp\xc4m'字节串U = u'sp\u00c4m'Unicode字符串S1 + S2拼接S * 3重复S索引S分片len(S)长度"a %s parrot" % kind字符串格式化表达式"a {0} parrot".format(kind)字符串格式化表达式S.find('pa')字符串方法:搜索S.rstrip()移除右侧空白S.replace('pa', 'xx')替换S.split(',')用分隔符分组S.isdigit()内容测试,是否为数字S.lower()大小写转换,转换为小写S.endwith('spam')尾部测试,是否以参数为结尾'spam'.join(strlist)分隔符连接S.encode('latin-1')Unicode编码B.decode('utf8')Unicode解码等for x in S: print(x)迭代'spam' in S成员关系成员关系map(ord, S)返回单个字符的ASCII序号`re.match('sp(.*)am', line)模式匹配:库模块除了上述工具集,Python还有支持更高级的字符串方法,如正则表达式匹配、XML解析器等的。
字符串字面量
Python的字符串有许多方法编写:
[*]单引号:'spa"m'
[*]双引号:"spa'm"
[*]三引号:'''...spam...''', """...spam..."""
[*]转义序列:"s\tp\na\0m"
[*]原始字符串:r"C:\new\test.spm"
[*]字节字面量:b'sp\x01am'
[*]Unicode字面量:u'eggs\u0020spam'
单引号和双引号字符串是一样的
Python支持单引号和双引号的原因是,解决字符串中含有"或'的问题。但是,如果字符串中没有单引号,最好使用单引号字符串(Python显示字符串时也是单引号)。
>>> "123"
'123'如果我们忘记了用逗号把字符串分开,那么Python会自动拼接相邻的字符串字面量:
>>> 'Meaning ', 'of ', 'life'
('Meaning ', 'of ', 'life')
>>> "Meaning " 'of ' "life"
'Meaning of life'如果字符串中既有单引号又有双引号,我们可以使用转义字符:
>>> 'knight\'s'
"knight's"
>>> "knight\"s"
'knight"s'转义序列代表特殊字符
反斜杠用来引入特殊的字符编码,称为转义序列。它帮助我们在字符串中嵌入不容易通过键盘输入的字符。看上去,以\为开头的转义字符占两个字节,但其实只占一个。
直接在交互界面上输入字符串变量,不会解释转义字符,print解释转义字符。
>>> s = 'a\nb\tc'
>>> s
'a\nb\tc'
>>> print(s)
a
b c上面的字符串包含5个字符,分别是:a、换行、b、制表、c。但不是5字节(因为字符串以Unicode编码)。反斜杠没有存进去。
Python的转义字符序列如下表:
转义意义\newline被省略(行的延续)\\范斜杠(保留一个\)\'单引号\"双引号\a响铃\b退格\f换页\n换行\r回车\t水平制表符\v垂直制表符\xhh十六进制值hh的字符\ooo八进制值为ooo的字符\0空字符\N{ id }Unicode数据库ID\uhhhh16位十六进制的Unicode字符\Uhhhhhhhh32位十六进制的Unicode字符\other不转义,保留\和\other嵌入绝对二进制数值的操作,注意到,Python在计算字符串长度时不会以到达空字符为标准,任何字符都不会结束Python的字符串。
>>> s = 'a\0b\0c'
>>> s
'a\x00b\x00c'
>>> len(s)
5不管如何指定不可打印字符(如空字符),Python的print都会把它们打印出来。
>>> S = "s\tp\na\x00m"
>>> S
's\tp\na\x00m'
>>> print(S)
s p
am如果Python认为\后面的字符不是有效的转义编码,那么它会生成字符串中的保留反斜杠:
>>> x = "C:\py\code"
>>> x
'C:\\py\\code'
>>> len(x)
10最好的办法是,如果要讲斜杠写进字符串中,则写反斜杠。
原始字符阻止转义
我们想打开一个绝对路径下的文件:
>>> myfile = open('C:\new\text.dat', 'w')此时,Python将其解释为:'C:',换行,'ew',制表符,'ext.dat'。要解决这个问题(保留反斜杠),可以用双反斜杠,或者在前字符串面加一个r。
>>> r'C:\new\text.dat'
'C:\\new\\text.dat'三引号编写多行块字符串
三引号可以便捷地编写多行的文本。
>>> mantra = """Always look
... on the bright
... side of life."""
>>> mantra
'Always look\non the bright\nside of life.'
>>> print(mantra)
Always look
on the bright
side of life.注意到,对这种连续的行而言,交互提示符会变成...(在编写函数、循环时也会遇到这种情形)。
实际上,三引号字符串会保留所有范围的文本,包括注释。
>>> menu = """spam # comments here added to string!
... eggs
... """
>>> menu
'spam\t\t# comments here added to string!\neggs\n'三引号的文本块也可以被当成注释,我们可以用三引号注释掉多行代码(此时Python运行时跳过这些被三引号注释掉的代码)。
实际应用中的字符串
基本操作
字符串的长度、用+拼接、用*重复:
>>> len('abc')
3
>>> 'abc' + 'def'
'abcdef'
>>> 'Ni!' * 4
'Ni!Ni!Ni!Ni!'可以用for循环对字符串的每个字符迭代,其中的in表示字符c用于指代字符串中的各个元素;可以用in方法检测字符串是否为字串(返回布尔值,而str.find()返回数字):
>>> myjob = "hacker"
>>> for c in myjob: print(c, end=' ')
...
h a c k e r
>>> "k" in myjob
True
>>> "z" in myjob
False
>>> "spam" in "abcspamdef"
True索引和分片
Python的偏移量从0开始,以比字符串长度小于1的偏移量结束,且支持负偏移量。分片表示从偏移i到偏移j-1的子串,其中如果i省略了,那么默认为0;如果j省略了,那么默认为字符串的长度。
>>> S = 'spam'
>>> S, S, S[-2]
('p', 's', 'a')
>>> S, S, S[:-1]
('pa', 'pam', 'spa')索引的图示:
细节:
[*]索引S:第一个元素偏移量为0,S[-i]表示倒数第i个字符;
[*]分片S提取序列的连续部分:包含下边界i,不包含上边界j,i、j缺省时默认为0和字符串的长度;
[*]扩展分片S:接受一个步长为k的字符串,默认值为1。
扩展分片:第三个限制值和分片对象
X表示:提取X中的全部元素,从偏移量i到j-1,每隔k个元素索引一次,k默认为+1。
比如,X会取出X中偏移量为1-9之间,每隔一个元素的元素,它收集X, X, X, X, X。
>>> S = 'abcdefghijklmnop'
>>> S
'bdfhj'当k为-1,我们可以反转字符串,实际上,当k为复数,从右(j-1)到左(i)提取:
>>> S = 'hello'
>>> S[::-1]
'olleh'分片的另外一种形式:使用slice函数转换元组。
>>> 'spam'
'pa'
>>> 'spam'
'maps'字符串转换工具
字符串不能和数字相加。但是,我们让数字和字符串相互转换:
>>> int("42"), str(42)
(42, '42')
>>> repr(42)
'42'int将字符串转换为数字,str将数字转换为字符串。repr函数有些特殊,它将一个对象作为参数,返回可作为代码的字符串对象,也就是说,可以运行该字符串重建被转换的对象。
>>> str('spam')
'spam'
>>> repr('spam')
"'spam'"因此,我们可以手动转换类型:
>>> S = '42'
>>> I = 1
>>> S + I
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
>>> int(S) + I
43
>>> S + str(I)
'421'字符串代码转换
ord将单个字符转换为ASCII码,chr则相反。
>>> ord('s')
115
>>> chr(115)
's'字符转换为数字后,可以进行运算。这样,我们就可以手动地实现字符串转换为整数的过程(循环会在后面介绍)。
>>> S = '114514'
>>> I = 0
>>> while S != '':
... I = I * 10 + (ord(S) - ord('0'))
... S = S
...
>>> I
114514修改字符串 I
不能在原位置修改字符串。
>>> S = 'spam'
>>> S = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment若要改变字符串(替换字串),我们可以使用拼接和分片的操作:
>>> S = S + 'SPAM!' # 通过拼接在后面添加字符串
>>> S
'spamSPAM!'
>>> S = S[:4] + 'Burger' + S[-1] # 通过分片、索引、拼接替换字符
>>> S
'spamBurger!'替换字符:
>>> S = 'splot'
>>> S = S.replace('pl', 'pamal')
>>> S
'spamalot'字符串格式化表达式创建新的文本值,有两种方式:
>>> 'That is %d %s bird!' % (1, 'dead')
'That is 1 dead bird!'
>>> 'That is {0} {1} bird!'.format(1, 'dead')
'That is 1 dead bird!'可以看作是,把对象转换为字符串,再根据指定的方式改变最初的字符串。
字符串方法
方法调用语法
方法是与特定对象相关联,并作用于特定对象的简单函数。它们是附属于对象的属性,引用的是可调用函数。调用方法时,结合两次操作:一次属性获取和一次函数调用。
[*]属性获取:object.attribute
[*]调用表达式:function(arguments)
合并两者可以调用一个对象方法。方法调用表达式为object.method(arguments)。Python首先获取对象object的方法attrinute,然后调用它,传递进对象object和参数arguments。即使用参数调用方法来处理对象。
字符串的方法
以下是Python 3.3的字符串方法,其中[]表示方括号里面的内容可以填,也可以省略:
S.capitalize() S.ljust(width [, fill])
S.casefold() S.lower()
S.center(width [, fill]) S.lstrip()
S.count(sub [, start [, end]]) S.maketrans(x[, y[, z]])
S.encode(]) S.partition(sep)
S.endswith(suffix [, start [, end]]) S.replace(old, new [, count])
S.expandtabs() S.rfind(sub [,start [,end]])
S.find(sub [, start [, end]]) S.rindex(sub [, start [, end]])
S.format(fmtstr, *args, **kwargs) S.rjust(width [, fill])
S.index(sub [, start [, end]]) S.rpartition(sep)
S.isalnum() S.rsplit(])
S.isalpha() S.rstrip()
S.isdecimal() S.split(])
S.isdigit() S.splitlines()
S.isidentifier() S.startswith(prefix [, start [, end]])
S.islower() S.strip()
S.isnumeric() S.swapcase()
S.isprintable() S.title()
S.isspace() S.translate(map)
S.istitle() S.upper()
S.isupper() S.zfill(width)
S.join(iterable)字符串方法示例:修改字符串II
上面我们讲过,我们可以用分片和拼接的组合,或者是replace方法来替换子字符串。事实上,replace可以替换字符串中的所有匹配的字串:
>>> 'aa$bb$cc$dd'.replace('$', 'SPAM')
'aaSPAMbbSPAMccSPAMdd'因此,replace可以实现模板替换。
如果要替换任意偏移量处出现的字串,可以再做一次替换(使用find搜索字串、分片拼接):
>>> S = 'xxxxSPAMxxxxSPAMxxxx'
>>> where = S.find('SPAM')
>>> where
4
>>> S = S[:where] + 'EGGS' + S[(where+4):]
>>> S
'xxxxEGGSxxxxSPAMxxxx'find函数找出字符串中第一个匹配字串的偏移量,没找到时返回-1。
但是,这种方法每次操作都需要创建一个新的字符串对象,时间代价跟打。因此我们可以将字符串转换为可修改的对象(list将任意序列转换为列表,元素的顺序不变):
>>> S = 'spammy'
>>> L = list(S)
>>> L
['s', 'p', 'a', 'm', 'm', 'y']再在列表中修改字符:
>>> L = 'x'
>>> L = 'x'
>>> L
['s', 'p', 'a', 'x', 'x', 'y']然后,在将其转换回一个字符串。join方法将列表中的字符/字符串从左到右拼接为一个字符串:
>>> S = ''.join(L)
>>> S
'spaxxy'join方法:
>>> 'SPAM'.join(['eggs', 'sausage', 'ham', 'toast'])
'eggsSPAMsausageSPAMhamSPAMtoast'字符串方法示例:解析文本
如果某些分隔符分开了数据组件,我们可以通过分割split拿出这些组件。
>>> line = 'aaa bbb ccc'
>>> cols = line.split()
>>> cols
['aaa', 'bbb', 'ccc']split方法将一个字符串从分隔符处切成一系列字串。默认的分隔符为空白。也可以根据实际情分割字符串:
>>> line = 'bob,hacker,40'
>>> line.split(',')
['bob', 'hacker', '40']实际应用中的其他常见字符串方法
清除空白(以strip结尾):
>>> line = "a b c "
>>> line.strip()
'a b c'
>>> line.rstrip()
'a b c'大小写转换:
>>> line.upper()
'AJAKSOSKASA'
>>> line.lower()
'ajaksoskasa'
>>> line.title()
'Ajaksoskasa'是否复合某一特征(以is开头):
>>> line = "abs"
>>> line.isalpha()
True是否以特定字符串为开头/结尾:
>>> line = "The knights who say Ni!\n"
>>> line.endswith('Ni!\n')
True
>>> line.startswith('The')
True替代的技巧可以取得与字符串方法相同的效果。如:S[-len(sub):] == sub相当于S.endswith(sub)。
原始string模块的函数(在Python 3.X中被删除)
在Python出现的最初10年,Python只提供string模块,这在之后成为了遗留问题。由于这部分涉及的方面过于古老,故不做相关笔记。
字符串格式化表达式
字符串格式化允许在单个步骤中对一个字符串执行多个特定类型的替换。如今,Python中的字符串格式化可以用两种方式实现:
[*]字符串格式化表达式:'...%s...' % (values)'
[*]字符串格式化方法调用:'...{}...'.format(values)'
这一节主要讲第一种方式。
格式化表达式基础
应用在字符串时,%运算符提供了根据格式定义,将不同类型的值格式化为字符串的简单方法。
格式化字符串:
[*]在%运算符左侧放置一个需要进行格式化的字符串,这个字符串带有一个或多个内嵌的转换目标,以%开头(如%d)。
[*]在%运算符右侧放置一个(或多个内嵌在元组中的)对象,这些对象会插入到左侧的字符串中,替换其中的转换目标。
比如,在这个例子中,整数1替换格式化字符串左边的%d,字符串'dead'替换%s,得到一个新的字符串。
>>> 'That is %d %s bird!' % (1, 'dead')
'That is 1 dead bird!'需要注意,所有的类型适用于%s的替换(但主要用于字符串),且当一个值插入时,直接在%右侧写下这个值;多个值插入时,需要元组。
>>> exclamation = 'Ni'
>>> 'The knights who say %s!' % exclamation
'The knights who say Ni!'
>>> '%d %s %g you' % (1, 'spam', 4.0)
'1 spam 4 you'
>>> '%s -- %s -- %s' % (42, 3.14159, )
'42 -- 3.14159 -- '高级格式化表达式语法
我们可以在格式化表达式中使用下表列出的任何一种转换类型的代码,它们出现在转换目标中的%的后面,学过C语言的会非常熟悉。
代码意义s字符串、任何对象的str(X)字符串r字符串、任何对象的repr(X)字符串c字符(int或str)d十进制i整数u同do八进制整数x十六进制整数X同xe带有指数的浮点数(科学计数法)E同ef十进制浮点数F同fg浮点数e或fG浮点数E或F%%字面量转换目标的一般结构是这样的(方括号表示可以省略):
%[(keyname)][.precision]typecode
其中,keyname为表示索引在表达式右侧使用的字典提供键名称;flags说明格式的标签,有左对齐-、数值符号+、正数前的空白以及负数前的符号(右对齐) 、零填充0;width为被替换的文本给出总的最小字段宽度,.precision为浮点数设置小数点后面的数位。
width和precision可以设置为*,表示它们应该从表达式右侧的输入值的下一项取值(而这个项用于替换*)。
高级格式化表达式举例
这个例子显示了flags的作用:
>>> x = 1234
>>> res = 'integers: ...%d...%-6d...%06d...% 6d' % (x, x, x, x)
>>> res
'integers: ...1234...1234...001234...1234'显示浮点数的三种方式,其中%e显示指数(科学计数法),%g根据数字内容选择格式,如果指数小于-4或者不小于精度,那么用%e,否则用%f。
>>> x = 1.23456789
>>> '%e | %f | %g' % (x, x, x)
'1.234568e+00 | 1.234568 | 1.23457'使用*在表达式右侧中得到宽度与精度:
>>> '%f, %.2f, %.*f' % (1/3.0, 1/3.0, 4, 1/3.0)
'0.333333, 0.33, 0.3333'基于字典的格式化表达式
字符串格式化允许左边的目标引用右边编写的字典中的键来提取对应的值:
>>> '%(qty)d more %(food)s' % {'qty': 1, 'food': 'spam'}
'1 more spam'左边通过格式化字符串中的(qty)和(food)分别引用右边字典的键'qty'和'food',提取对应的值。
我们可以建立一个字典,并利用单个基于键的引用的格式化表达式一次性替换它们。这种小技巧一般配合var()使用,var()返回的字典包含了所有存在的变量:
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'exclamation': 'Ni', 'x': 1.23456789, 'res': 'integers: ...1234...1234...001234...1234'}字符串格式化方法调用
字符串格式化方法基础
在Python 2.6之后的版本中,可以使用字符串的format方法,它是函数调用而不是表达式。
在主体字符串中,花括号通过位置、关键字或相对位置指定替换目标和将要插入的参数。
通过位置替换:
>>> template = '{0}, {1} and {2}'
>>> template.format('spam', 'ham', 'eggs')
'spam, ham and eggs'通过关键字替换:
>>> template = '{motto}, {pork} and {food}'
>>> template.format(motto='spam', pork='ham', food='eggs')
'spam, ham and eggs'通过相对位置替换:
>>> template = '{}, {} and {}'
>>> template.format('spam', 'ham', 'eggs')
'spam, ham and eggs'看上去,上一节的字符串格式化表达式更简洁。本质上,格式化方法中字符串的{}相当于上一节的%s,任何类型可以在目标上替换。
>>> '{motto}, {0} and {food}'.format(42, motto=3.14, food=)
'3.14, 42 and 'format方法也是创建一个新的字符串。
添加键、属性和偏移量
格式化方法调用也是可以扩展。例:
>>> import sys
>>> 'My {1} runs {0.platform}'.format(sys, {'kind': 'laptop'})
'My laptop runs win32'
>>> 'My {map} runs {Sys.platform}'.format(Sys=sys, map={'kind': 'laptop'})
'My laptop runs win32'点表示通过位置或关键字引用的元素的对象属性,格式化字符串中的方括号可以指定字典的键,也可以指定列表的偏移量来执行索引。
>>> somelist = list('SPAM')
>>> 'first={0}, third={0}'.format(somelist)
'first=S, third=A'高级格式化方法语法
如下,是可以在一个格式化字符串中作为可替代目标出现的形式化结构:
{fieldname component !conversionfig :formatspec}
其中,
[*]fieldname是辨识参数的一个可选的数字或关键字;
[*]component是有着若干个/零个".name"或引用的字符串,其中.name
获取参数的方法,获取参数的属性/索引值;
[*]conversionfig如果出现,前面会带!,后面跟着r,s或a,分别调用repr、str和ascii函数;
[*]formatspec如果出现,则以:开始,后面跟着一些文本。
formatspec的格式如下:(这里中括号表示可以不填)
[align][#][,][.precision]
其中,
[*]fill可以是任意的填充字符(除了{和});
[*]align可以是、=或^,分别表示左对齐、右对齐、前面的符号字符填充、居中;
[*]sign可以是+、-或空格,用于显示数值类型的符号;
[*]width和precision与字符串格式化表达式相同;
[*]typecode可以参照介绍字符串格式化表达式时给出的表,也可以是b,用于表示二进制。
高级格式化方法举例
{0:10}表示第一个参数至少占一个10字符宽的字段,{1:10}意味着第一个参数的platform属性在10字符宽的字段中右对齐。这里使用了dict方法创建字典:
>>> import sys>>> '{0:10} = {1:10}'.format('spam', 123.4567)'spam = 123.4567'>>> '{0:>10} = {1:10} = {1:
页:
[1]