Python CookBook第二章 字符串和文本

目录

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'\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.

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'

你可能感兴趣的:(学习笔记,python,开发语言)