py3——字符串和文本

http://python3-cookbook.readthedocs.io

分割字符串

?将一个字符串分割为多个字段,但是分隔符并不是固定的:

对于常规的字符串分割情形,string 对象的 split方法足以胜任

In [1]: str = "hello,my name is amy,what are you name?"
In [2]: str.split(",")
Out[2]: ['hello','my name is amy','what are you name?']

如果要求将字符串分割成单词呢?

In [3]: import re
In [4]: re.split(r'[,\s?]',str)
Out[4]: ['hello','my','name','is','amy','what','are', 'you','name','']

字符串开头或结尾匹配

?通过指定的文本模式去检查字符串的开头或者结尾

简单的匹配开头或者结尾:

In [5]: filename = 'test.txt'
In [6]: filename.endswith('.txt')
Out[6]: True
In [7]: filename.startswith('.txt')
Out[7]: False

简单的多种匹配:

In [8]: filename.endswith(('.txt','.py','.c'))
Out[8]: True

该方法必须输入一个元组作为参数,如果你有一个 list 或 set 类型的选择项,可以用 tuple() 方法转换

通配符匹配字符串

所谓的通配符就是正则表达式中的 . / * / ? 等匹配符
在 fnmatch 模块中提供了 fnmatch() 和 fnmatchcase() 两个函数可以帮助我们很好的来运用这些通配符

In [9]: from fnmatch import fnmatch,fnmatchcase

In [10]: fnmatch('foo.txt','*.txt')
Out[10]:True
In [11]: fnmatch('foo.txt','?oo.txt')
Out[11]:True

fnmatch 函数使用底层操作系统的大小写敏感规则(不同系统是不一样的)
如果你对大小写匹配特别看重的话,可以使用 fnmatchcase 来进行匹配

字符串匹配和搜索

?想要匹配或者搜索特定模式的文本

使用正则表达式对字符串进行复杂的匹配和搜索

In [12]: import re
In [13]: text1 = '11/27/2012'
In [14]: text2 = 'Nov 27, 2012'

In [15]: if re.match(r'\d+/\d+/\d+',text1):print('yes')
In [16]: else:print('no')
Out[16]:yes
In [17]: if re.match(r'\d+/\d+/\d+',text2):print('yes')
In [18]: else:print('no')	
Out[18]:no

如果,你想用同一个模式去做多次匹配,那么先将模式字符串预编译为模式对象是一个很合适的选择

In [19]: datepat = re.compiler(r'\d+/\d+/\d+')
In [20]: if datepat.match(text1):print('yes')
In [21]: else:print('no')
Out[21]: yes
In [22]: if datepat.match(text2):print('yes')
In [23]: else:print('no')
Out[23]: no

match() 总是从字符串开始去匹配,如果你想查找字符串任意部分的模式出现位置,使用 findall() 方法去代替,findall返回的是一个列表对象,如果你想以迭代的方式返回匹配,可以使用 finditer() 方法来代替。如果想从字符串任意部分来匹配一个模式对象,可以使用 search() 方法。search严格执行从左到右开始匹配,如果匹配到就返回第一个匹配对象,如果匹配不到就返回None

In [24]: import re
In [25]: str = "my name is amy, what are you name ?"
In [26]: print(re.match('amy',str))
Out[26]: None
In [27]: print(re.match('my',str))
'my'

In [28]: re.findall("name",str)
In [29]: ['name','name']
In [30]: re.findall("amy",str)
In [31]: ['amy']

In [32]: re.search("amy",str)
Out[32]: <_sre.SRE_Match object; span=(11,14),match='amy'>
In [33]: re.search("name",str)
Out[33]: <_sre.SRE_Match object; span=(3,7),match='name'>
In [34]: re.search("name",str).group()
Out[34]: 'name' 

字符串搜索和替换

?在字符串中搜索和匹配指定的文本模式
对于简单的字面模式,直接使用 str.replace() 方法即可。

In [35]: text = "Hi,what do you like to drink ? milk?"
In [36]: text.replace('milk','orange juice')
Out[36]: 'Hi,what do you like to drink ? orange juice?'

对于复杂的模式,则使用 re 模块中的 sub() 函数。

In [37]: text = 'Today is 2/14/2018, New Year`s Eve is on 2/15/2018.'
In [38]: re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)  #如果不太理解参考 python 正则匹配部分知识
Out[38]: 'Today is 2018-2-14, New Year`s Eve is on 2018-2-15.'

sub() 函数中的第一个参数是被匹配的模式,第二个参数是替换模式。反斜杠数字比如 \3 指向前面模式的捕获组号。

如果你想用一个模块做多次匹配,可以参考前面的先编译。
##字符串忽略大小写的搜索替换

?当需要忽略大小写的方式搜索与替换文本字符串

re 模块中的 IGNORECASE 参数能够解决这个问题

In [39]: text = 'Hello,python.hello world!'
In [40]: re.findall('hello',text,flags=re.IGNORECASE)
Out[40]: ['Hello','hello']
In [41]: re.sub('hello','nihao',text,flags=re.IGNORECASE)
Out[41]: 'nihao,python.nihao world!'

在上面的例子中,替换后的字符串大小写并没有与原文大小写保持一致。可以动动脑写一个辅助函数来修复这一点小缺陷。
##最短匹配模式
?当使用正则时,一个模式可能会匹配出一个长匹配,也可能会匹配出一个短匹配,当你的目标是短匹配时:
比如 ‘I have two girlfriends,“yang” and “kui”’

text = ' I have two girlfriends,"yang" and "kui" '
re.findall(r'\".*\"',text)
[' "yang" and "kui" ']

怎么能达到匹配 “yang” “kui” 这样的呢

re.findall(r'\".*?\"',text)

只需要加一个?就能解决这种问题,在正则中 * 是代表贪婪模式的,就是尽可能多的去匹配。加了个 ? 就变成非贪婪模式了。
##多行匹配模式

?当你试图用正则去匹配一大块文本,需要跨越多行去匹配

正则中 . 已经近乎成为一个万能匹配符,然而单单不能匹配换行符,这时可以做如下处理:

text1 = "<hello,my name is amy and i like drinking milk.>"
#python中三引号支持换行
text2 = '''<hello,my name is amy 
and i like drinking milk.>'''

re.findall(r'<(.*?)>',text1)
['hello,my name is amy and i like drinking milk.']

re.findall(r'<(.*?)>',text2)
[]

re.findall(r'<((?:.|\n)*?)>',text)
['hello,my name is amy \nand i like drinking milk.']

(?:.|\n) 制定了一个非捕获组,也就是说仅仅用来匹配而不是单独捕获或者编号的组,另外 re.compile() 函数接收一个标志参数 re.DOTALL 它可以让(.)匹配包括换行符在内的任意字符
##将Unicode文本标准化

?在处理unicode字符串时,确保所有字符串在底层有相同的表示

因为在unicode中,某些字符能够用多个合法的编码表示

>>> s1 = 'Spicy Jalape\u00f1o'
>>> s2 = 'Spicy Jalape\u0303no'
>>> s1
'Spicy Jalapeño'
>>> s2
'Spicy Jalapeño'
>>> s1 == s2
False
>>> len(s1)
14
>>> len(s2)
15
>>>

在需要比较字符串的程序中使用字符的多种表示会产生问题,为了修正这种问题,可以使用unicodedata模块将文本标准化

>>> import unicodedata
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFC', s2)
>>> t1 == t2
True
>>> print(ascii(t1))
'Spicy Jalape\\xf1o'
>>> t3 = unicodedata.normalize('NFD', s1)
>>> t4 = unicodedata.normalize('NFD', s2)
>>> t3 == t4
True
>>> print(ascii(t3))
'Spicy Jalapen\\u0303o'
>>>

normalize() 第一个参数指定字符串标准化的方式。NFC表示字符应该是整体组成(比如可能的话就使用单一编码),而NFD表示字符应该分解为多个组合字符表示。
##在正则式中使用unicode
默认情况下,re模块已经对一些unicode字符类有了基本的支持,比如,\d已经匹配任意的 unicode 数字字符

>>> import re
>>> num = re.compile('\d+')
>>> # ASCII digits
>>> num.match('123')
<_sre.SRE_Match object at 0x1007d9ed0>
>>> # Arabic digits
>>> num.match('\u0661\u0662\u0663')
<_sre.SRE_Match object at 0x101234030>
>>>

如果你想在模式中包含指定的unicode字符,可以使用unicode字符对应的转义序列(比如 \uFFF 或者 \uFFFFFFF)。

>>> arabic = re.compile('[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff]+')

删除字符串中不需要的字符

?去掉文本字符串开头,结尾或者中间不想要的字符,比如空白。

strip() 方法能用于删除开始或结尾的字符。 lstrip() 和 rstrip() 分别从左从右和从右到左执行删除操作。默认情况下,这些方法会去除空白字符,也可以指定其他字符。

>>> # Whitespace stripping
>>> s = ' hello world \n'
>>> s.strip()
'hello world'
>>> s.lstrip()
'hello world \n'
>>> s.rstrip()
' hello world'
>>>
>>> # Character stripping
>>> t = '-----hello====='
>>> t.lstrip('-')
'hello====='
>>> t.strip('-=')
'hello'
>>>

strip()方法在读取和清理数据时并不会对字符串的中间的文本产生任何影响。
##审查清理文本字符串

在涉及文本解析和数据处理等一系列问题时,在简单的情况下。可能会选用到一些字符串函数来将文本标准化,比如 str.upper() 和 str.lower() 。或者是使用 str.replace() 和 str.sub() 的简单替换操作来删除或者改变指定的字符序列。
当你想消除整个区间上的字符或者去除变音符。可以使用str.translate()方法。

>>> s = 'pýtĥöñ\fis\tawesome\r\n'
>>> s
'pýtĥöñ\x0cis\tawesome\r\n'
>>>

第一步清理空白字符。可以创建一个小的转换表格然后使用translate()方法:

>>> remap = {
...     ord('\t') : ' ',
...     ord('\f') : ' ',
...     ord('\r') : None # Deleted
... }
>>> a = s.translate(remap)
>>> a
'pýtĥöñ is awesome\n'
>>>

第二步,删除所有的和音符

import unicodedata
import sys
# 将特殊字符转换为空
# unicodedata.combining() 将分配给字符chr的规范组合类作为整数返回。 如果未定义组合类,则返回0。
# dict.fromkeys 创建字典
cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) if unicodedata.combining(chr(c)))
...
>>> b = unicodedata.normalize('NFD', a)
>>> b
'pýtĥöñ is awesome\n'
>>> b.translate(cmb_chrs)
'python is awesome\n'
>>>

字符串对齐

以某种对齐方式来格式化字符串

对于基本的字符串对齐操作,可以使用字符串的 ljust() , rjust() , center() 方法。

>>> text = 'Hello World'
>>> text.ljust(20)
'Hello World         '
>>> text.rjust(20)
'         Hello World'
>>> text.center(20)
'    Hello World    '

对于以上方法都可以接受一个可选的填充字符:

text.rjust(20,'=')
'==============Hello World'

另一种方法format也可以实现这种格式化操作

>>> format(text, '>20')
'         Hello World'
>>> format(text, '<20')
'Hello World         '
>>> format(text, '^20')
'    Hello World     '
>>>

format指定非空格填充字符

>>> format(text, '=>20s')
'=========Hello World'
>>> format(text, '*^20s')
'****Hello World*****'
>>>

format还可以同时对多个值进行格式化

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

format的另一个好处是它不仅适用于字符串,还可以用来格式化任何值,比如说数字:

>>> x = 1.2345
>>> format(x, '>10')
'    1.2345'
>>> format(x, '^10.2f')
'   1.23   '

##字符串对齐

将几个小的字符串合并为一个大的字符串

  • 要合并的字符串是一个序列或者 iterable 中,可以使用 join() 方法
strlist = ['h','e','l','l','o']
"".join(strlist)   # 将列表中字符直接拼接起来
",".join(strlist)  # 将列表中字符用,隔开拼接起来
  • 合并少数字符串 + 号连接(不推荐)
a = 'good'
b = 'morning'
a + b
  • 复杂的字符串格式化操作可以用format

字符串中插入变量

创建一个内嵌变量的字符串,用变量的值所对应的替换变量。

  • 简单的替换直接使用 format
s = 'good {time} ,what day is today ? {name}'
s.format(time='morning',name='kai')
  • 被替换的变量能在变量域中找到
time = "morning"
name = "kai"
s = 'good {time} ,what day is today ? {name}'
s.format_mp(vars())

varsity也适用于对象实例

class Infodef __init__(self,time,name)
		self.name = name
		self.time = time

a = Info("morning","kai")
s.format_map(vars(a))
format 和 format_map() 的缺陷:

format 和 format_map() 并不能很好的处理变量缺失的情况
避免这种错误的方法是另外定义一个含有 missing() 方法的字典对象

class safesub(dict):
	def __missing__(self,key):
		# 这里如果确实变量就不做替换
		return "{"+key+"}"

time = "morning"
s = 'good {time} ,what day is today ? {name}'
s.format_mp(safesub(vars()))
# 'good morning ,what day is today ? {name}'

以指定列宽格式化字符串

将一些长字符串以指定的列宽重新格式化。

使用 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."
# 下面演示使用 textwrap 格式化字符串的多种方式:

>>> import textwrap
>>> 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.

AND
textwrap 模块对于字符串打印是非常有用的,特别是当你希望输出自动匹配终端大小的时候。 你可以使用 os.get_terminal_size() 方法来获取终端的大小尺寸。比如:

>>> import os
>>> os.get_terminal_size().columns
80

fill() 方法接受一些其他可选参数来控制tab,语句结尾等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值