Python CookBook第二章 字符串和文本

Python CookBook第二章 字符串和文本,第1张

目录

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在字节串上进行文本 *** 作


2.1针对任意多的分隔符拆分字符串
#使用正则结合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']
2.2在字符串的开头或结尾处作文本匹配
# 使用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
#书上说还可以使用正则表达式,确实可以,但是在这种条件下明显这样侠侣会更高一些
2.3利用Shell通配符做字符串匹配
#一般常见的*.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]']
2.4文本模式的查找和匹配
# 这一块的内容主要使用正则进行比较复杂的匹配,比如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
2.5查找和替换文本
# 简单的文本替换可以使用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'--',text3)  #反斜线表示在捕获组中的数量
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.
2.6以不区分大小写方式进行查找和替换
#使用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
2.7定义实现最短的匹配正则表达式
#其实这一块就是说一下正则表达式中的贪婪模式(.*)和惰怠模式(.*?),熟悉一点的大家都会明白
#小小例子说明一下
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'] #只匹配符合条件的内容
2.8编写多行模式的正则表达式
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 ']
2.9将Unicode 文本统一标表示为规范形式
#如果在文本中包含多种字符,不进行规范化处理回对进一步处理产生很大的困扰
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)
2.10使用正则表达式进行处理Unicode字符
# 就是利用正则表达式与特殊字符结合对文本内容进行匹配
import re
ad = re.compile('[\u0600-\u06ff-\u0750-\u077f\u08a0-\u08ff]+')
# 更多精彩内容请解锁re模块库
2.11 从字符串中去掉不需要的字符
# 这一块我们经常用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
2.12 文本的过滤和清理
# 这一小节介绍的较为高级的方法,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
2.13对齐文本字符
# 基本 *** 作使用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
2.14 字符串连接及合并
# 对于简单的连接来说使用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
#这一小节难点在于判断自己的数据的复杂程度,从而选择性价比高的方式,使用迭代器将会产生更少的垃圾

2.15 给字符串中的变量名做插值处理
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方法
2.16以固定的列数格式化文本
# 较之之前的方法我可能会写一个循环 将内容进行切片写入新的文件
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
2.17在文本中处理HTML和XML实体
#是对内部的特殊字符进行转义,成文本内容  比如<> &
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 >>>
#其实在本小节的内容多数会在爬虫中用到,但是爬虫的相关模块对这些内容已经有了很好的编译,不用太费劲’
2.18 文本分词
# 这一块主要运用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')
#如果在进行匹配时,有较长的匹配内容中含有较短的匹配如(>=)和(=) ,应该先保证较长的进行匹配,否则会落下内容
2.20在字节串上进行文本 *** 作
# 一般来说用字符串进行 *** 作就好,较少的用字节串,这样更好的符合现代要求,,()说白了,了解一下就行?_?
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'

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/langs/794842.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存