目录
2.1针对任意多的分隔符拆分字符串
2.2在字符串的开头或结尾处作文本匹配
2.3利用Shell通配符做字符串匹配
2.4文本模式的查找和匹配
2.5查找和替换文本
2.6以不区分大小写方式进行查找和替换
2.7定义实现最短的匹配正则表达式
2.8编写多行模式的正则表达式
2.9将Unicode 文本统一标表示为规范形式
2.10使用正则表达式进行处理Unicode字符
2.11 从字符串中去掉不需要的字符
2.12 文本的过滤和清理
2.13对齐文本字符
2.14 字符串连接及合并
2.15 给字符串中的变量名做插值处理
2.16以固定的列数格式化文本
2.17在文本中处理HTML和XML实体
2.18 文本分词
2.20在字节串上进行文本操作
#使用正则结合split的re.split()对多种分隔符进行分解
line = 'you are,my;the most beautiful girl'
import re
ad = re.split(r'[; , \s]\s*',line) #注意这里是[]
print(ad) #['you', 'are', 'my', 'the', 'most', 'beautiful', 'girl']
#小心捕获组 就是小括号用滴
ab = re.split(r'(,|;|\s)\s*',line)
print(ab) #['you', ' ', 'are', ',', 'my', ';', 'the', ' ', 'most', ' ', 'beautiful', ' ', 'girl']
#将分隔符与字符串进行分离操作
values = ab[::2]
delimiters = ab[1::2]
print(values,'\n',delimiters)
# ['you', 'are', 'my', 'the', 'most', 'beautiful', 'girl']
# [' ', ',', ';', ' ', ' ', ' ']
#用了捕获组还不想变样子,那只能这么做
aq = re.split(r'(?:,|;|\s)\s*',line)
print(aq) #['you', 'are', 'my', 'the', 'most', 'beautiful', 'girl']
# 使用startswith()or endswith() 对文件进行检索
filename = 'niuniu.txt'
print(filename.endswith('.txt')) #Ture
url = 'http://www.baidu.com'
print(url.startswith('http:')) #Ture
#更便利的找到目标文件类型
import os
filenames = os.listdir('.')
print(filenames) #['somefile.txt', '第一章 数据结构和算法.py', '第二章 字符串和文本.py']
print(list(name for name in filenames if name.endswith('.py'))) #['第一章 数据结构和算法.py', '第二章 字符串和文本.py']
#在网页读取中我们可以这样分辨
from urllib.request import urlopen
def read_data(name):
if name.startswith(('http:','https','ftp:')):
return urlopen(name).read()
else:
with open(name) as f:
return f.read()
# 如果选择的条件是放在列表或者集合中,必须先将其转换为元组tuple(),否则会报错哦
limits = ['http:','https','ftp:']
url = 'http://ww.baidu.com'
print(url.startswith(tuple(limits))) #Ture
#书上说还可以使用正则表达式,确实可以,但是在这种条件下明显这样侠侣会更高一些
#一般常见的*.py [0-9]*.csv 等
#本小节介绍的为 fnmatch()和fnmatchcase()两个函数
from fnmatch import fnmatch,fnmatchcase
print(fnmatch('somefile.txt','*.txt')) #True 在本机路径下有这个文件
print(fnmatch('第一章 数据结构和算法.py','*.py')) # Ture
names = ['data1.csv','data2.csv','baibai.htlm','speder1.py']
print(list(name for name in names if fnmatch(name,'*.csv'))) #使用list列表将其生成器转换为列表 ['data1.csv', 'data2.csv']
#fnmatch()在不同系统中对大小写又不一样结果,Mac中会区分大小写.txt 和.TXT结果不同 ,但是Windows中不会,结果都一样,使用fnmatchcase()可以对大小写进行区分
#在查找字符串列表会有奇效
mails = [
'[email protected]','[email protected]','[email protected]',
'[email protected]','[email protected]','[email protected]'
]
from fnmatch import fnmatchcase
print(list(mail for mail in mails if fnmatchcase(mail,'*@163.*'))) #结果九四这个样子事儿的['[email protected]', '[email protected]', '[email protected]']
# 这一块的内容主要使用正则进行比较复杂的匹配,比如match,findall,finditer,find,search等较为简单的就是一些str.find(),str.startswith(),str.endswith()等函数
text = 'you,are so beautiful so that i am falling in love'
print(text.startswith('you')) #True
print(text.endswith('i')) #False
print(text.find('love')) #45
#使用re进行更为复杂的匹配
import re
text1 = '2022/4/29,today is sunny,I will have a date in 2022/5/1.'
m = re.match(r'\d+/\d+/\d+',text1)
print(m) #返回一个
print(m.group()) #2022/4/29 使用group() 取出,正如之前所学。match总是尝试在字符串的开头寻找返回想要的东西,想要全文搜索 还得看下边这位
#先进行一个预备式
rdat = re.compile(r'(\d+)/(\d+)/(\d+)') #为了方便处理,将文字以捕获组的形式表达,这样每个组的地方都可取出来
f = rdat.findall(text1)
print(f) #['2022/4/29', '2022/5/1']
for year,month,day in f:
print('{}-{}-{}'.format(month,day,year))
#4-29-2022
# 5-1-2022
#还可以使用finditer()函数生成迭代器 给他单个支棱出来
for df in rdat.finditer(text1):
print(df.group())
#2022/4/29
# 2022/5/1
# 简单的文本替换可以使用str.replace(),
text2 = 'haer,what do you want to eat today?'
print(text2.replace('haer','Baby')) #Baby,what do you want to eat today?
#稍稍进阶一点的做法使用re.sub()
text3 = '2022/4/29,today is sunny,I will have a date in 2022/5/1.'
import re #当然也可以准备一个预备式re.compile('balbla')
t1 = re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\2-\1',text3) #反斜线\3表示在捕获组中的数量
print(t1) #29-4-2022,today is sunny,I will have a date in 1-5-2022.
#更加复杂的情况可以使用一个回调函数 由match()或find()函数,group抽取进行替换 返回更改后的格式
from calendar import month_abbr
rdat = re.compile('(\d+)/(\d+)/(\d+)')
def change_data(m):
mon_name = month_abbr[int(m.group(2))]
return '{} {} {}'.format(m.group(3),mon_name,m.group(1))
t2 = rdat.sub(change_data,text3)
print(t2) #29 Apr 2022,today is sunny,I will have a date in 1 May 2022.
#使用re 模块中的re.IGNORECASE
text = 'UPPER PYTHON,lower python,Mixed Python'
import re
f1 = re.findall('python',text,flags=re.IGNORECASE)
print(f1) #['PYTHON', 'python', 'Python']
t1 = re.sub('python','java',text,flags=re.IGNORECASE)
print(t1) #UPPER java,lower java,Mixed java
#虽然上述方法实现了字符的查找和替换,但是存在不匹配的结果 前后不一致 。这样就需要一个函数对其进行重新匹配
def matchcase(word): #这个函数就是很简单的一个,不用多解释了吧。。。记得这里需要先引入re模块,前边也有 这里就没写
def replace(m):
text = m.group()
if text.isupper():
return word.upper()
elif text.islower():
return word.lower()
elif text[0].isupper():
return word.capitalize()
else:
return word
return replace
t2 = re.sub('python',matchcase('java'),text,flags=re.IGNORECASE)
print(t2) #UPPER JAVA,lower java,Mixed Java
#其实这一块就是说一下正则表达式中的贪婪模式(.*)和惰怠模式(.*?),熟悉一点的大家都会明白
#小小例子说明一下
text = '"haer",what, do you "want", to eat today?'
import re
r1 = re.compile(r'\"(.*)\"')
print(r1.findall(text))#['haer",what, do you "want']贪婪模式附尽可能的想要匹配到所有的内容,包含错误的
r2 = re.compile(r'\"(.*?)\"')
print(r2.findall(text))# ['haer', 'want'] #只匹配符合条件的内容
text = '''/* this is a
multiline comment */
'''
import re
comment = re.compile(r'/\*(.*?)\*/')
print(comment.findall(text)) #[]没有匹配到结果
com = re.compile(r'/\*((?:.|\n)*?)\*/') #(?:.|\n)指定一个非捕获组,只做匹配,不分配组号,不捕获结果
print(com.findall(text)) #[' this is a\n multiline comment ']
#有一种简单的方式可以做简单的处理 re.DATALL 这个东西使得re 中的(.*)可以匹配任何字符,包括换行符
comment = re.compile(r'/\*(.*?)\*/',re.DOTALL)
print(comment.findall(text)) #[' this is a\n multiline comment ']
#如果在文本中包含多种字符,不进行规范化处理回对进一步处理产生很大的困扰
s1 = 'Spicy Jalape\u00f1o' #'Spicy Jalapeño'
s2 = 'Spicy Jalapen\u0303o' #'Spicy Jalapeño'
print(s1 == s2) #False
import unicodedata
t1 = unicodedata.normalize('NFC',s1)
t2 = unicodedata.normalize('NFC',s2)
print(t1 == t2) #True
print(ascii(t1)) #'Spicy Jalape\xf1o'
print(ascii(t2)) #'Spicy Jalape\xf1o'
t3 = unicodedata.normalize('NFD',s1)
t4 = unicodedata.normalize('NFD',s2)
print(ascii(t3)) #'Spicy Jalapen\u0303o'
print(ascii(t4)) #'Spicy Jalapen\u0303o'
#在unicodedata中存在多种规范表示方式 NFD NFKC和NFKD为特定的字符制定了额外的兼容功能
s = '\ufb01'
t5 = unicodedata.normalize('NFKC',s)
t6 = unicodedata.normalize('NFKD',s)
print(t5,t6)
# 就是利用正则表达式与特殊字符结合对文本内容进行匹配
import re
ad = re.compile('[\u0600-\u06ff-\u0750-\u077f\u08a0-\u08ff]+')
# 更多精彩内容请解锁re模块库
# 这一块我们经常用strip()函数就可以解决大多数问题 本小节介绍lstrip()和rstrip()函数,分别从左边和右边删除,默认为空格
s = 'hello world \n'
print(s.strip()) #hello world
print(s.rstrip()) #hello world
s1 = '----hello===='
print(s1.lstrip('-').rstrip('=')) #hello
#然鹅,strip这个函数不能对文本之间的空白进行处理,这可肿么办捏?
s2 = 'hello world '
print(s2.strip()) #hello world
print(s.replace(' ','')) #hello world 原来是三个空格,替换掉了两个
import re
print(re.sub('\s+','',s2)) #helloworld
# 如果对文本进行处理时,读取文件的每一行然后依次抽取内容
with open('somefile.txt','r') as f:
lines = [line.strip() for line in f]
for line in lines:
pass
# 这一小节介绍的较为高级的方法,str.translate(),但是一般的使用2.9小节内容基本可以解决,但是更加较为复杂的内容使用这一块内容性能会更佳。
q = 'phthon\fis\tawesome\r\n'
#首先建立一个小型的转换表
change_lis = {
ord('\f') : ' ',
ord('\r') : None,
ord('\t') : ' '
}
#然后使用translate进行转化
print(q.translate(change_lis)) #phthon is awesome \f和\t被重新映射为一个空格 \r呗完全删除掉了
# 加载一个庞大的转换表,把所有的Unicode字符全部去掉
import unicodedata
import sys
change_dict = dict.fromkeys(c for c in range(sys.maxunicode)\
if unicodedata.combining(chr(c)))
b = unicodedata.normalize('NFD',q)
print(b) #phthon is awesome 首先进行规范化
print(b.translate(change_dict)) #phthon is awesome 进行转化为None
#还有将unicode十进制字符映射为相应的ASCII版本
digmap = {
c:ord('0') + unicodedata.digit(chr(c)) for c in range(sys.maxunicode)\
if unicodedata.category(chr(c)) == 'Nd'
}
print(len(digmap)) #650
x = '\u0661\u0662\u0663'
print(x.translate(digmap)) #123
# 基本操作使用ljust(),rjust()和ccenter()方法就够了
text = 'hello world'
print(text.rjust(20)) # hello world
print(text.center(20)) # hello world
#也可以指定字符
print(text.ljust(20,'=')) # hello world=========
print(text.center(20,'*')) #****hello world*****
#略微难一点的使用format格式化方式也可以的,他可以对任何值进行对齐操作,老一些的版本中还会有%来进行格式化
print(format(text,'>20')) # hello world '>'右对齐
print(format(text,'=^20s')) #====hello world===== '^'居中对齐
print(format(text,'=<20s'))#hello world========= '<'左对齐
#遇到多个字符时
print('{:>10s} {:>10s}'.format('hello','world')) # hello world
# 对于简单的连接来说使用join()和+就可以实现
parts = ['you','are','so','beautiful!']
print(' '.join(parts)) #you are so beautiful!
a = 'da dan'
b = 'er dan'
c = a+' '+b
print(c) #da dan er dan
print('{} {}'.format(a,b)) #da dan er dan
#这一小节难点在于判断自己的数据的复杂程度,从而选择性价比高的方式,使用迭代器将会产生更少的垃圾
name = 'Frank'
n = 28
print(f"{name}'s age is {n}") #format()格式化 Frank's age is 28
#值在变量中能够在找到,format和vars进行连用
s = "{name}'s age is {n}"
print(s.format_map(vars())) #Frank's age is 28
#缺点是没有办法优雅的处理缺少某个值的情况 所以就需要创建一个带有__missing__()方法的字典类
class safesub(dict):
def __missing__(self, key):
return '{'+ key +'}'
del n #整个缺值
print(s.format_map(safesub(vars()))) #Frank's age is {n}
#如果有必要的话,可以这样设置一个‘frame hack‘
import sys
def sub(text):
return text.format_map(safesub(sys._getframe(1).f_locals))
print(sub('your favorite color is {color}')) #your favorite color is {color}这样你就不会报错 而且不用反复去定义一个miss方法
# 较之之前的方法我可能会写一个循环 将内容进行切片写入新的文件
p = ' '
nov = p
size = len(nov)
offset =0
chunk = 70
while True:
if offset > size:
break
f.write(nov[offset:offset+chunk] + '\n')
offset += chunk
#在这一节中 采用textwrap模块以多种方式重新格式化字符串:
t = '混沌未分天地乱,茫茫渺渺无人见。 自从盘古破鸿蒙,开辟从兹清浊辨。 覆载群生仰至仁,发明万物皆成善。 欲知造化会元功,须看《西游释\
厄传》。 盖闻天地之数,有十二万九千六百岁为一元。将一元分为十二会,乃子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥之十二支也。每会该一万\
八百岁。且就一日而论:子时得阳气,而丑则鸡鸣;寅不通光,而卯则日出;辰时食后,而巳则挨排;日午天中,而未则西蹉;申时晡而日落酉;戌黄昏而人定\
亥。譬于大数,若到戌会之终,则天地昏曚而万物否矣。再去五千四百岁,交亥会之初,则当黑暗,而两间人物俱无矣,故曰混沌。又五千四百岁,亥会将终,\
贞下起元,近子之会,而复逐渐开明。'
import textwrap
print(textwrap.fill(t,40)) #输出以40个字符伟一行
print(textwrap.fill(t,40,initial_indent='*')) #开头用啥开头欧
print(textwrap.fill(t,40,subsequent_indent='*')) #m每一行的这个东西用啥
# 关于终端的尺寸大小
import os
# os.get_terminal_size().columns
#是对内部的特殊字符进行转义,成文本内容 比如<> &
s = 'elements are written as "text ".'
import html
print(s)
print(html.escape(s)) #elements are written as "<tag>text</tag>".
print(html.escape(s,quote = False)) #elements are written as "<tag>text</tag>".
#如果是想生成ASCII文本,针对非ASCII文本进行转换
s = 'Spicy Jalapeno'
s.encode('ascii',errors = 'xlmcharrefreplace')
print(s)
#想要进行乱码转义,则需要以下的样式
s = 'elements are written as "<tag>text</tag>".'
import html
print(html.unescape(s)) #elements are written as "text ".
t = 'go go go >>>'
from xml.sax.saxutils import unescape
print(unescape(t)) #go go go >>>
#其实在本小节的内容多数会在爬虫中用到,但是爬虫的相关模块对这些内容已经有了很好的编译,不用太费劲’
# 这一块主要运用re模块对数据进行过滤,主要难点是掌握每一个符号的含义,捕获组命名抽取?P.
import re
name = r'(?P[a-zA-Z_][a-zA-Z_0-9]*)'
num = r'(?P\d+)'
plus = r'(?P\+)'
times = r'(?P\*)'
EQ = r'(?P=)'
WS = r'(?P\s+)'
master_pat = re.compile('|'.join([name,num,plus,times,EQ,WS]))
#然后使用scanner()方法完成分词操作,它会重复调用match(),每次换一个模式
a = 'foo = 43'
scanner = master_pat.scanner('foo = 43')
sc = scanner.match()
print(sc.lastgroup,sc.group()) #name foo
#将其转换为迭代器
from collections import namedtuple
Tokens = namedtuple('Taken',['type','value'])
def generrate_tokens(pat,text):
scanner = pat.scanner(text)
for m in iter(scanner.match,None):
yield Tokens(m.lastgroup,m.group())
for tok in generrate_tokens(master_pat,'foo = 43'):
print(tok)
# Taken(type='name', value='foo')
# Taken(type='WS', value=' ')
# Taken(type='EQ', value='=')
# Taken(type='WS', value=' ')
# Taken(type='num', value='43')
#如果想要过滤莫一种标记流。。。 以下是一个过滤所有空格的迭代器
tokens = (tok for tok in generrate_tokens(master_pat,'foo = 43') if tok.type != 'WS')
for tok in tokens:
print(tok)
# Taken(type='name', value='foo')
# Taken(type='EQ', value='=')
# Taken(type='num', value='43')
#如果在进行匹配时,有较长的匹配内容中含有较短的匹配如(>=)和(=) ,应该先保证较长的进行匹配,否则会落下内容
# 一般来说用字符串进行操作就好,较少的用字节串,这样更好的符合现代要求,,()说白了,了解一下就行?_?
data = b'Holle World'
print(data[0:5]) #b'Holle'
print(data.split()) #[b'Holle', b'World']
print(data.replace(b'World',b'Girl')) #b'Holle Girl'
# 也可以使用re进行匹配,但是需要b''
re.split(b'[,;]',data)
#如果想要格式化,得先把它变成字符串,然后在进行格式化
print('{:10s} {:10d} {:10.2f}'.format('Alice',100,29.1).encode('ascii')) #b'Alice 100 29.10'