2. python字符串的奇淫巧技

  1. 字符串分割

默认我们会使用Split方法进行字符串的分割,这种方式必须要传入一个单一的分隔符进行分割,如果希望更加灵活的分割字符串,最好使用re.split()方法,其支持传入多个正则模式,只要符合如何一个模式就会返回,返回的类型跟split一致

比如

>>> line = ‘asdf fjdk; afed, fjek,asdf, foo’

>>> import re

>>> re.split(r'[;,\s]\s*’, line)

上面我们就在其中指定了可以匹配,逗号,分号 乃至是空格

如果是括号捕获分组,那么匹配的文本也会在结果队列

r'(;|,|\s)\s*’

  1. 字符串开头匹配

如果是简单的进行开头结尾匹配,那么就是使用str.startwith() 或者 str.endwith()

>>> filename = ‘spam.txt’

>>> filename.endswith(‘.txt’)

同时支持输入一个元组作为参数进行匹配,比如

name.startswith((‘http:’, ‘https:’, ‘ftp:’))

这就需要注意,如果是一个list或者set类型的选择项,需要先使用tuple将其转换为元组类型

当然,我们也可以使用re.match函数进行相关的匹配,不过这样有些大材小用了

>>> re.match(‘http:|https:|ftp:’, url)

  1. 使用Shell通配符匹配

如果想要使用shell的*通配符匹配,则可以使用fnmatch进行实现

>>> fnmatch(‘foo.txt’, ‘*.txt’)

>>> fnmatch(‘foo.txt’, ‘?oo.txt’)

需要注意的是,这种方式不同操作系统的大小写敏感规则是不一样的

比如mac或者linux上,是大小写敏感的,window上则不一样

如果需要强匹配,则可以使用fnmatchcase()代替,完全使用模式大小写匹配

>>> fnmatchcase(‘foo.txt’, ‘*.TXT’)

  1. 字符串的匹配

字符串匹配只需要调用基本字符串就可以了,比如str.find() endswith() startswith()

当然,更加复杂的匹配需要使用正则表达式和re模块

比如我们的日期字符串为

>>> text1 = ’11/27/2012′

这时候我们可以利用re.match进行匹配

>>> if re.match(r’\d+/\d+/\d+’, text1):

如果希望多次利用一个模式进行匹配,这时候则建议进行编译

>>> datepat = re.compile(r’\d+/\d+/\d+’)

需要注意match是进行匹配,而如果想要获取符合模式的字符串,则更加应该使用findall函数

除了findall,还有着finditer(),返回一个迭代方式

如果希望忽略大小写进行匹配,则可以在操作的时候传入re.IGNORECASE标志

  1. 字符串的替换

如果是进行字符串的替换的话,简单可以使用str.replace函数

如果是复杂的模式,则是需要使用re.sub函数

可以进行多匹配,其需要多个参数,第一个参数是匹配模式,第二个参数是替换模式

比如

>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)’)

>>> datepat.sub(r’\3-\1-\2′, text)

我们进行了分组匹配,然后在替换模式中\3是前面的捕获组号

>>> text = ‘Today is 11/27/2012. PyCon starts 3/13/2013.’

‘Today is 2012-11-27. PyCon starts 2013-3-13.’

如果需要进行多次替换,则可以考虑先编译从而提升性能

还支持传入回调函数来进行替换

>>> def change_date(m):

… mon_name = month_abbr[int(m.group(1))]

… return ‘{} {} {}’.format(m.group(2), mon_name, m.group(3))

>>> datepat.sub(change_date, text)

‘Today is 27 Nov 2012. PyCon starts 13 Mar 2013.’

回调函数中的m

是其中匹配的一组,方便我们进行替换

最后还有一个re.subn函数,可以查看多少替换发生了

同样,python的正则表达式支持贪婪和非贪婪匹配

就是简单的正则匹配上加上 ?修饰符

这样就是要求这个匹配是非贪婪的

  1. Unicode文本标准化

由于Unicode中,某些字符可以使用多个编码来进行表示

>>> s1 = ‘Spicy Jalape\u00f1o’

>>> s2 = ‘Spicy Jalapen\u0303o’

>>> s1

‘Spicy Jalapeño’

>>> s2

‘Spicy Jalapeño’

>>> s1 == s2

False

所以我们需要进行文本标准化,这一部分我们使用的unicodedata模块下的normalize函数

比如我们上面展示的s1,s2,可以进行转换

>>> import unicodedata

>>> t1 = unicodedata.normalize(‘NFC’, s1)

>>> t2 = unicodedata.normalize(‘NFC’, s2)

>>> t1 == t2

True

第一个参数表示了标准化的方式,支持NFC NFKD NFKC通过不同的标准化方式来使用不同的兼容特性

这里顺便提一嘴,虽然python支持在正则中使用unicode字符,但是由于支持的并不太好,所以最好使用一些第三方库进行支持

  1. 字符串的删除

简单的删除可以使用replace,替换特定字符串为空字符即可,或者是使用sub函数也可以

除此之外,我们可以使用strip来删除开头或者结尾的字符串

其中支持传入多个字符串

>>> s = ‘ hello world \n’

>>> s.strip()

‘hello world’

>>> t = ‘—–hello=====’

>>> t.strip(‘-=’)

‘hello’

除了这个之外,还有lstrip()函数和rstrip()函数

如果希望更多的替换方式,还可以使用translate()方法

对于这个translate方法,支持传入一个字典,进行使用

创建一个小的转换表格,进行使用translate方法

>>> s = ‘pýtĥöñ\fis\tawesome\r\n’

>>> remap = {

… ord(‘\t’) : ‘ ‘,

… ord(‘\f’) : ‘ ‘,

… ord(‘\r’) : None # Deleted

… }

>>> a = s.translate(remap)

>>> a

通过这样一个映射表,完成了相关的映射,从而将空白字符进行了删除

  1. 字符串格式化

对于字符串的格式化操作,如果只是需要对齐,直接使用ljust() rjust() center()

对应的是左对齐,右对齐以及居中对齐

>>> text = ‘Hello World’

>>> text.ljust(20)

‘Hello World ‘

>>> text.center(20)

‘ Hello World ‘

或者使用format函数,从而对字符串

比如

>>> format(text, ‘>20’)

‘ Hello World’

同样希望左对齐则是<,

居中对齐则是 ^

还支持指定一个对齐字符串,只需要在上面的特殊字符前面加 填充字符即可

>>> format(text, ‘=>20s’)

‘=========Hello World’

>>> format(text, ‘*^20s’)

‘****Hello World*****’

还支持格式化多个值

>>> ‘{:>10s} {:>10s}’.format(‘Hello’, ‘World’)

‘ Hello World’

那么就这个format函数,相比较老版本的 % 运算符,功能更为强大

  1. 字符串的合并

我们希望将多个字符串合并为一个大的字符串

此时我们最好能够将多个字符串放在一个序列或者iterable中

然后使用字符串的join方法

>>> parts = [‘Is’, ‘Chicago’, ‘Not’, ‘Chicago?’]

>>> ‘ ‘.join(parts)

Is Chicage Not Chicago?

当然,直接使用+ 也可以

>>> a = ‘Is Chicago’

>>> b = ‘Not Chicago?’

>>> a + ‘ ‘ + b

但是这样性能并不如join那么好

最后考虑下,如果想要编写构建大量小字符串的输出代码,可以考虑使用yield语句产生输出片段

def sample():

yield ‘Is’

yield ‘Chicago’

yield ‘Not’

yield ‘Chicago?’

text = ”.join(sample())

  1. format函数

对于一些需要在字符串中声明变量,并在后续进行替换的问题的需求,可以考虑使用fotmat函数进行解决,比如

>>> s = ‘{name} has {n} messages.’

>>> s.format(name=’Guido’, n=37)

‘Guido has 37 messages.’

如果在当前能够访问的变量域中有对应的变量值的话,那么可以直接使用format_map加vars()

>>> name = ‘Guido’

>>> n = 37

>>> s.format_map(vars())

‘Guido has 37 messages.’

或者我们使用vars() 将一个对象实例包起来使用

>>> class Info:

def __init__(self, name, n):

self.name = name

self.n = n

>>> a = Info(‘Guido’,37)

>>> s.format_map(vars(a))

但是在format运行过程中,如果存在着变量缺失的情况,那么会抛出KeyError

对于这种问题,可以考虑定义一个对象,继承字典

class safesub(dict):

“””防止key找不到”””

def __missing__(self, key):

return ‘{‘ + key + ‘}’

然后将原本的对象进行包装后,传递给format_map()

更进一步可以利用方法进行包装format

import sys

def sub(text):

return text.format_map(safesub(sys._getframe(1).f_locals))

这里我们使用sys._getframe(1)来获取调用者的栈帧,并且访问f_locals获取其中的局部变量,这种情况并不推荐,但是还是声明一下,这样的操作是访问到了一个复制本地变量的字典,并不会影响原本的变量,所以还是具有安全性保证的。

  1. 指定输出列宽

使用textwrap模块来格式化字符串的输出,比如有如下的长字符串

s = “Look into my eyes, look into my eyes, the eyes, the eyes, \

the eyes, not around the eyes, don’t look around the eyes, \

look into my eyes, you’re under.”

我们希望指定输出行宽,则可以如下

>>> print(textwrap.fill(s, 70))

Look into my eyes, look into my eyes, the eyes, the eyes, the eyes,

not around the eyes, don’t look around the eyes, look into my eyes,

you’re under.

>>> print(textwrap.fill(s, 40))

Look into my eyes, look into my eyes,

the eyes, the eyes, the eyes, not around

the eyes, don’t look around the eyes,

look into my eyes, you’re under.

>>> print(textwrap.fill(s, 40, initial_indent=’ ‘))

Look into my eyes, look into my

eyes, the eyes, the eyes, the eyes, not

around the eyes, don’t look around the

eyes, look into my eyes, you’re under.

>>> print(textwrap.fill(s, 40, subsequent_indent=’ ‘))

Look into my eyes, look into my eyes,

the eyes, the eyes, the eyes, not

around the eyes, don’t look around

the eyes, look into my eyes, you’re

under.

这一方法很适合于字符串打印,如果是希望输出的宽度自动匹配终端大小的时候,那么直接在textwrap.fill中填入os.get_terminal_size()方法即可

  1. 字节字符串和字符串

字节字符串支持很多和文本字符串一样的内置操作,比如

>>> data = b’Hello World’

>>> data[0:5]

b’Hello’

即使是正则表达式,也是支持匹配字节字符串的,但是需要注意,正则表达式本身也要是字符串

但是需要注意,直接读取字节字符串,会得到对应的字节值,比如

>>> b = b’Hello World’ # Byte string

>>> b[0]

72

但是字节字符串虽然速度很快,但是需要注意,字节字符串在很多的时候,会由于无法自动的编解码导致问题

所以仍然不建议使用

发表评论

邮箱地址不会被公开。 必填项已用*标注