文件操作
冯诺依曼体系架构

- CPU由运算器和控制器组成
- 运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理
- 控制器,控制计算机各部件协调运行
- 存储器,用于记忆程序和数据,例如内存
- 输入设备,将数据或者程序输入到计算机中,例如键盘、鼠标
- 输出设备,将数据或程序的处理结果展示给用户,例如显示器、打印机等
一般说IO操作,指的是文件IO,如果指的是网络IO,都会直接说网络IO
文件IO常用操作

打开操作
open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closefd= True, opener=None)
- 打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常
- 基本使用:
创建一个文件test,然后打开它,用完关闭
f = open("tese") # file对象
# windows <_io.TextIOWrapper name='tese' mode='r' encoding='cp936'>
# linux <_io.TextIOWrapper name='tese' mode='r' encoding='UTF-8'>
print(f.read()) # 读取文件
f.close() # 关闭文件
12345
- 文件操作中,最常用的操作就是 “读” 和 “写”
- 文件访问的模式有两种:
- 文本模式和二进制模式。不同模式下,操作函数不相同,表现的结果也不一样
注: - windows中使用codepage代码页,可以认为每一个代码页就是一张编码表。cp936等同于GBK
open的参数
- file
- 打开或者要创建的文件名。如果不指定路径,默认是当前路径
- mode模式

在上面的例子中,可以看到默认是文本打开模式,且是只读的
r 模式
- 只读打开文件,如果使用write方法,会抛异常
- 如果文件不存在,抛出FileNotFoundError异常
- open模式是只读模式r打开已存在的文件

w 模式
- 表示只写打开方式,如果读取则抛出异常
- 如果文件不存在,则直接创建文件
- 如果文件存在,则清空文件内容

x 模式
- 文件不存在,创建文件,并只写方式打开
- 文件存在,抛出FileExistsError异常

a 模式
- 文件存在,只写打开,追加内容
- 文件不存在,则创建后,只写打开,追加内容

总结
- r 是只读,wxa 都是只写
- wxa 模式都可以产生新文件
w 不管文件存在与否,都会生成全新内容的文件
a 不管文件是否存在,都能在打开的文件尾部追加
x 必须要求文件事先不存在,自己创建一个新文件
文本模式 t
- 字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是 rt
二进制模式 b
- 字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型
f = open("test3",'rb') # 二进制只读
s = f.read()
print(type(s)) # bytes
print(s)
f.close() # 关闭文件
f = open("tese3",'wb') # IO对象
s = f.write("你好".encode()) # encode默认使用UTF-8编码,一个中文3个字节
print(s) # 6
f.ckose()
12345678910
+ 模式
- 为r、w、a、x提供缺失的读或写功能,但是获取文件对象依旧按照r、w、a、x自己的特征
- +模式不能单独使用,可以认为它是为前面的模式字符做增强功能的
文件指针
- 文件指针,指向当前字节位置
- mode=r,指针起始在0
- mode=a,指针起始在EOF
- tell()显示指针当前位置
- seek(offset[,whence]) 移动文件指针位置。offest偏移多少字节,whence从哪里开始
文本模式下
whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest只接受0
whence 2 表示从EOF开始,offest只接受0
- 文本模式支持从开头向后偏移的方式
- whence为1表示从当前位置开始偏移,但是只支持偏移0,相当于原地不动,所以没什么用
- whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF
- seek是按照字节偏移的
- read在文本模式是按照字符读取的
二进制模式下
whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest可正可负
whence 2 表示从EOF开始,offest可正可负
二进制模式支持任意起点的便宜,从头、从尾、从中间位置开始
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常
buffering:缓冲区
- -1表示使用缺省大小的buffer。如果是二进制模式,使用价格io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192。如果是文本模式,如果是终端设备,实行缓存方式,如果不是,则使用二进制模式的策略
- 0,只在二进制模式使用,表示关buffer
- 1,只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush
- 大于1,用于指定buffer的大小
buffer缓冲区
- 缓冲区一个内存空间,一般来说是一个FIFO队列,到缓冲区满了或者达到阈值,数据才会flush到磁盘。
fluch()
- 将缓冲区数据写入磁盘
chlose()
- 关闭前会调用flush()
- io.DEFAULT_BUFFER_SIZE 缺省缓冲区大小,字节
二进制模式
import io
f = open('test','w+b')
print(io.DEFAULT_BUFFER_SIZE) # 8192
f.write("hello".encode())
# cat test
f.seek(0) # 指针回0
# cat test
f.write('python'.encode())
f.flush()
f.close()
f = open('test','w+b',4) # 缓冲区大小
f.write(b'hello')
# cat test
f.write(b'python')
# cat test
f.close()
12345678910111213141516171819
文本模式
import io
# buffering=1,使用行缓冲
f = open('test','w+',1)
f.write('hello') # cat test
f.write('python'*4) # cat test
f.write('\n') # cat test
f.write('hi\nPython') # cat test
f.close()
#buffering>1,使用指定大小的缓冲区
f = open('test','w+',15)
f.write('hello') # cat test
f.write('python') # cat test
f.write('hi\n') # cat test
f.write('\nPython') # cat test
f.write('a' * (io.DEFAULT_BUFFER_SIZE - 20)) # 设置为大于1没什么用
f.write('\nhelloPython')
f.close()
12345678910111213141516171819
- buffering=0
- 这是一种特殊的二进制模式,不需要内存的buffer,可以看作是一个FIFO的文件
f = open('test','wb+',0)
f.write('hello') # cat test
f.write('python'*4) # cat test
f.write('\n') # cat test
f.write('hi\nPython') # cat test
f.close()
123456

- 文本模式,一般都用默认缓冲区大小
- 二进制模式,是一个个字节的操作,可以指定buffer的大小
- 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
- 一般编程中,明确知道需要写磁盘了,都会手动调用一次flush,而不是等到自动flush或者close的时候
encoding:编码,仅文本模式使用
- None表示使用缺省编码,依赖操作系统。windows、linux下测试如下代码
f = open('test','w')
f.write('啊')
f.close()
123
windows下缺省GBK(0xB0A1),linux下缺省UTF-8(0xE5 95 8A)
其他参数
errors
- 什么样的编码错误将被捕获
- None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略
newline
- 文本模式中,换行的转换。可以用None、“空串、’\r’、’\n’、’\r\n’
- 读时,None表示’\r’、’\n’、’\r\n’都被转换为’\n’;’'空串表示不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按照指定字符分行
- 写时,None表示’\n’都会被替换为系统缺省行分割符os.linesep;’\n’或’‘空串表示’\n’不替换;其它合法字符表示’\n’会被替换为指定的字符
f = open('/etc/test', 'w')
# newline缺省为None,windows下会把\n替换为\r\n
f.write('python\rwww.python.org\nwww.baidu.com\r\npython3')
# 真正写入的是
# 'python\rwww.python.org\rwww.baidu.com\r\rpython3'
f.close()
newlines = [None, '', '\n', '\r\n']
for nl in newlines:
f = open('/etc/test', newline=nl) # 缺省替换所有换行符
print(f.readlines())
f.close()
# 运行结果如下
# ['python\n', 'www.python.org\n', 'www.baidu.con\n', '\n', 'python3']
# 常见换行符都替换为\n
# ['python\r', 'www.python.org\r\n', 'www.baidu.con\r', '\r\n', 'python3']
# ''表示什么都不做
# ['python\rwww.python.org\r\n', 'www.baidu.con\r\r\n', 'python3']
# \n做作为换行符
# ['python\rwww.python.org\r\n', 'www.baidu.con\r\r\n', 'python3']
# \r\n作为换行符
123456789101112131415161718192021
closefd
- 表示文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。fileobj.fileno()查看
read
- read(size=-1)
- size表示读取的多少个字符或字节;负数或者None表示读取到EOF
f = open('/etc/test', 'r+')
f.write("hello")
f.write('\n')
f.write('你好')
f.seek(0)
f.read(7)
f.close()
# 二进制
f = open('/etc/test', 'rb+')
f.read(7)
f.read(1)
f.close()
123456789101112
行读取
readline(size=1)
- 一行行读取文件内容。size设置一次能读取行内几个字符或字节
readlines(hint=-1)
- 读取所有行的列表。指定hint则返回指定的行数
# 按行迭代
f = open('test') # 返回可迭代对象
for line in f:
print(line.encode())
f.close()
1234567
write
- write(s),把字符串s写入到文件中并返回字符的个数
- writelines(lines),将字符串列表写入文件
f = open('test', 'w+')
line = ['abc', '123\n', 'hello'] # 提供换行符
f.writelines(lines)
f.seek(0)
print(f.read())
f.close()
12345678
close
- flush并关闭文件对象
- 文件已经关闭,再次关闭没有任何效果
其他

上下文管理
- 在Linux中,执行
lst = []
for _ in range(2000):
lst.append(open('test'))
# OSError: [Errno 24] Too many open files: 'test'
print(len(lst))
123456
lsof:列出打开的文件
没有就# yum install lsof
$ ps aux | grep python
$ lsof -p 9255 | grep test |wc -1
$ ulimit -a
123
- ps命令返回进程,grep处python进程ID
- lsof -p 进程号,列出该进程的所有文件描述符,grep出test文件的文件描述符,wc统计
- ulimit -a 查看所有限制。其中open files就是打开问价数的限制,默认1024
for x in lst:
x.close()
12
将文件一次关闭,然后就可以继续打开了。再看一次lsof
1、异常处理
- 当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是因为资源限制产生的
f = open('test')
try:
f.writs("abc") # 文件只读,写入失败
finally:
f.close() # 这样才行
12345
- 使用finally可以保证打开的文件可以被关闭
2、上下文管理
一种的特殊语法,交给解释器去释放文件对象
上下文管理
del f
with open('test') as f:
f.write('abc') # 文件只读,写入失败
#测试f是否关闭
f1.closed
123456
- 对于类似文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源
- IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会做限制。就是为了保护计算机的资源不要被完全耗尽,计算资源是共享的,不是独占的
- 一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题
StringIO
- io模块中的类
from io impoet StringIO - 内存中,开辟的一个文本模式的buffer,可以像文件对象一样操作它
- 当close方法被调用的时候,这个buffer会被释放
getvalue()
- 获取全部内容。跟文件指针没有关系
from io impoer StringIO
# 内存中构建
sio = StringIO() # 像文件对象一样操作
print(sio.readable(), sio.writable(), sio.seekable())
sio.write("hello\nPython)
sio.seek(0)
print(sio.readline())
print(sio.getvalue()) # 无视指针,输出全部内容
sio.close()
12345678910
好处
- 一般来说,磁盘的操作比内存的操作要慢的多,内存足够的情况下,一般的优化思想是少落地,减少磁盘IO的过程,可以大大提高程序的运行效率
BytesIO
- io模块中的类
from io impoet BytesIO - 内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它
- 当close方法被调用的时候,这个buffer会被释放
from io import BytesIO # 内存中构建
bio = BytesIO()
print(bio.readable(), bio.writable(), bio.seekable())
bio.write(b'hello\nPython')
bio.seek(0)
print(bio.readline())
print(bio.getvalue()) # 无视指针,输出全部内容
bio.close()
12345678
file-like对象
- 类文件对象、可以像文件对象一样操作
- socket对象、输入输出对象(stdin、stdout)都是类文件对象
from sys import stdout, stderr
f = stdout
print(type(f))
f.write('hello')
f.close()
路径操作模块
3.4版本之前
os.path模块
from os import path
p = path.join('/etc', 'sysconfig', 'network') # 拼接
pring(type(p), p)
print(path.exists(p)) # 存在
print(path.split(p)) # 分割
print(path.dirname(p), path.basename(p)) #路径和基名
print(path.abspath(''),path.abspath('.')) # 绝对路径
print(path.spltdrive('d:/temp/test')) # windows方法
p1 = path.abspath(__file__)
print(p1)
while p1 !=path.dirname(p1):
dirname = path.ditname(p1)
print(ditname)
p1 = dirname
123456789101112131415161718
3.4版本开始
- 建议使用pathlib模块,提供Path对象来操作。包括目录和文件
pathlib模块
- from pathlib import Path
目录操作
初始化
p = Path() # 当前目录, Path()、Path('.')、Path('')
p = Path('a', 'b', 'c/d') # 当前目录下的a/b/c/d
p = Path('/etc') # 根下的etc目录
123
路径拼接和分解
- 操作符 /
Path对象 / Path对象
Path对象 / 字符串 或者 字符串 / Path对象 - 分解
parts属性,可以返回路径中的每一个部分 - joinpath
joinpath(*other)连接多个字符串到Path对象中
from pathlib import Path
p = Path()
p = p / 'a'
p1 = 'b' / p
p2 = Path('c')
p3 = p2 / p1
print(p3.parts) # ('c', 'b', 'a')
p4 = p3.joinpath('etc', 'init.d', Path('httpd')) # c\b\a\etc\init.d\httpd
print(p4)
12345678910
获取路径
- str 获取路径字符串
- bytes 获取路径字符串的bytes
from pathlib import Path
p = Path('/etc')
print(str(p)) # \etc
print(bytes(p)) # b'\\etc'
12345
父目录
- parent目录的逻辑父目录
- parents父目录序列,可迭代对象,索引0是直接的父
from pathlib import Path
p = Path('a/b/c/d')
print(p.parent) # a\b\c
print(p.parent.parent) # a\b
for x in p.parents:
print(x)
# print(x)执行结果
# a\b\c
# a\b
# a
# .
123456789101112
目录组成部分
- name、stem、suffix、suffixes、with_suffix(suffix)、whit_name(name)
name 目录的最后一个部分
suffix 目录中最后一个部分的扩展名
stem 目录最后一个部分,没有后缀
name = stem + suffix
suffixes 返回多个扩展名列表
with_suffix(suffix) 有扩展名则替换,无则补充扩展名
whit_name(name) 替换目录最后一个部分并返回一个新的路径
from pathlib import Path
p = Path('/etc/python/mysql.tar.gz')
print(p.name) # mysql.tar.gz
print(p.suffix) # .gz
print(p.suffixes) # ['.tar', '.gz']
print(p.stem) # mysql.tar
print(p.with_name('python-5.tgz')) # \etc\python\python-5.tgz
print(p.with_suffix('.png')) # \etc\python\mysql.tar.png
p = Path('README')
print(p.with_suffix('.txt')) # README.txt
1234567891011
全局方法
- cwd() 返回当前工作目录
- home()返回当前家目录
判断方法
- exists() 目录或者文件是否存在
- is_dir() 是否是目录,目录存在返回True
- is_file() 是否是普通文件,文件存在返回True
- is_symlink() 是否是软链接
- is_socket() 是否是socket文件
- is_block_device() 是否是块设备
- is_char_device() 是否是字符设备
- is_absolute() 是否是绝对路径
绝对路径
- resolve() 非Windows,返回一个新的路径,这个新路径就是当前Path对象的绝对路径,如果是软链接则直接被解析
- absolute() 获取绝对路径
- rmdir() 删除空目录。没有提供判断目录为空的方法
- touch(mode=0o666, exist_ok=True) 创建一个文件
- as_uri() 将路径返回成URI,例如’file:///etc/passwd’
- mkdir(mode=0o777, parents=False, exist_ok=False)
- parents,是否创建父目录,True等同于mkdie -p。False时,父目录不存在,则抛出FileNotFoundError
- exist_ok参数,在3.5版本加入,False时,路径存在。抛出FileExistsError;True时,FileExistsError被忽略
- iterdir()迭代当前目录,不递归
from pathlib import Path
p = Path()
p /= 'a/b/c/d'
p.exists() # True
#创建目录
p.mkdir() # FileNotFoundError
p.mkdir(parents=True)
p.exists() # True
p.mkdir(parents=True)
p.mkdir(parents=True,exist_ok=True)
p /= 'readme.txt'
p.parent.exists() # False 'a/b/c'
p.mkdir() # FileNotFoundError
p.mkdir(parents=True) # 成功
# 遍历,判断文件类型,如果是目录是否可以判断其是否为空目录?
for x in p.parents[len(p.parents) - 1].iterdir():
print(x, end='\t')
if x.is_dir():
flag = False
for _ in x.iterdir():
flag = True
break
print('dir', 'Not Empty' if flag else 'Empyt',sep='\t')
elif x.is_file():
print('file')
else:
print('other file')
12345678910111213141516171819202122232425262728293031
通配符
- glob(pattern) 通配给定的模式
- rglob(pattern) 通配给定的模式,递归目录
- 都返回一个生成器
- ? 代表一个字符
- * 表示任意一个字符
- [abc]或[a-z]表示一个字符
list(p.glob('test*') # 返回当前目录对象下的test开头的文件
list(p.glob('**/*.py') # 递归所有目录,等同rglob
list(p.glob('**/*'))
g = p.rglob('*.py') # 生成器,递归
next(g)
list(p.rglob('*.???')) # 匹配扩展名为3个字符的文件
list(p1.rglob('[a-z]*.???')) # 匹配字母开头的且扩展名是3个字符的文件
12345678
匹配
- match(pattern)
- 模式匹配,成功返回True
from pathlib import Path
Path('a/b.py').match('*.py') # True
Path('/a/b/c.py').match('b/*.py') # True
Path('/a/b/c.py').match('a/*.py') # False
Path('/a/b/c.py').match('a/*/*.py') # True
Path('/a/b/c.py').match('a/**/*.py') # True
Path('/a/b/c.py').match('**/*.py') # True
12345678
- stat()相当于stat命令
- lstat()同stat(),但如果是符号链接,则显示符号链接本身的文件信息
# ln -s test t
from pathlib import Path
p = Path('test')
p.stat()
p1 = Path('t')
p1.stat()
p1.lstat()
12345678
文件操作
Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
1
使用方法类似内建函数open。返回一个文件对象
- 3.5增加的新函数
Path.read_bytes()
1
以’rb’读取路径对应文件,并返回二进制流。看源码
Path.read_test(encoding=None, errors=None)
1
以’rt’方式读取路径对应文件,返回文本
Path.write_bytes(data)
1
以’wb’方式写入数据到路径对应文件
Path.write_text(data, encoding=None, errors=None)
1
以’wt’方式写入字符串到路径对应文件
from pathlib import Path
p = Path('my_binary_file')
p.write_bytes(b'Binary file contents')
p.read_bytes() # b'Binary file contents'
p = Path('my_text_file')
p.write_text('Text file contents')
p.read_text() # 'Text file contents'
with p.open() ad f:
print(f.read(5))
123456789101112
os 模块
- 操作系统平台

- os.listdie(‘o:/temp’)
返回指定目录内容列表,不递归
os也有open、read、write等方法,但是太底层,建议使用内建函数open、read、write,使用方法相似
# 建立一个软链接
$ ln -s test t1
12
- os.stat(path, *, dir_fd=None,follow_symlinks=True)
- 本质上调用Linux系统的stat
- path:路径的string或者bytes,或者fd文件描述符
- follow_symlinks True返回文件本身信息,False且如果是软链接则显示软链接本身。可以使用os.lstat方法
os.stat('o:/test.txt')
os.stat_result(st_mode=33206, st_ino=281474976710698, st_dev=389224164, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1508808249, st_mtime=1508808249, st_crtime=1508808249)
st_mode=33206 => 100666
os.stat('test')
os.stat_result(st_mode=33204, st_ino=3407875, st_dev=64768, st_nlink=1, st_uid=500, st_gid=500, st_size=3, st_atime=1508690220, st_mtime=1508690177, st_crtime=1508690177)
12345
- os.chmod(path, mode, *, dit_fd=None, follow_symlinks=True)
- os.chmod(‘test’,0o777)
- os.chown(path,uid,gid)
改变文件的属主、属组,但需要足够的权限
shutil模块
- 文件拷贝:使用打开2个文件对象,源文件读取内容,写入目标文件中来完成拷贝过程。但是这样丢失stat数据信息(权限等),因为根本没有复制这些信息过去
- 如何目录复制,Python提供了一个方便的库shutil(高级文件操作)
copy复制
copyfileobj(fsrc, fdst[, length])
- 文件对象的复制,fsrc和fdst是open打开的文件对象,复制内容。fdst要求可写,length指定了表示buffer的大小
import shutil
with open('/etc/test', 'r+') as f1:
f1.write('abcd\n1234')
f1.flush()
f1.seek(0) # 指针回0
with open('/etc/test1', 'w+') as f2:
shutil.copyfileobj(f1,f2)
12345678
copyfileobj源码
def copygileobj(fsrc, fdst, length=16*1024):
"""copy data from file-like object fsrc to file-like object fdst"""
while 1:
buf = fsrc.read(length)
if not buf: # 如果为空则退出循环
break
fdst.write(buf)
1234567
copyfile(src, dst, *, follow_symlinks=True)
- 复制文件内容,不含元数据。src, dst为文件的路径字符串
- 本质上调用的就是copyfileobj,所以不带元数据二进制内容复制
copymode(src, dst, *, follow_symlinks=True)
- 仅仅复制权限

copystat(src, dst, *, follow_symlinks=True)
- 复制元数据,stat包含权限
copy(src, dst, *, follow_symlinks=True)
- 复制文件内容、权限和部分元数据,不包括创建时间和修改时间
- 本质上调用的是
copyfile(src, dst, follow_symlinks=follow_symlinks)
copymode(src, dst, follow_symlinks=follow_symlinks)
copy2
- 比copy多了复制全部元数据,但需要平台支持
- 本质上调用的是
copyfile(src, dst, follow_symlinks=follow_symlinks)
copystat(src, dst, follow_symlinks=follow_symlinks)
copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False)
- 递归复制目录。默认使用copy2,也就是带更多的元数据复制
- src、dst必须是目录,src必须存在,dst必须不存在
- ignore = func,提供一个callable(src, names) -> ignored_names。提供一个函数,它会被调用。src是源目录,names是os.listdir(src)的结果,就是列出src中的文件名,返回值是要被过滤的文件名的set类型数据
# /etc/temp下有a、b目录
def ignore(src, names):
ig = filter(lambda x: x.startswith('a'), names) # 忽略a开头的
return set(ig)
shutil.copytree('/etc/temp', '/etc/python/o', ignore=ignore)
123456
rm 删除
shutil.rmtree(path, ignore_errors=False, οnerrοr=None)
- 递归删除。如同rm -rf一样危险,慎用
- 它不是原子操作,有可能删除错误,就会中断,已经删除的就删除了
- ignore_errors为True,忽略错误。当为False或者omitted时onerror生效
- onerror为callable,接受函数function、path和execinfo
shutil.rmtree('/etc/bin') # 类似 rm -rf
1
move 移动
move(src, dst, copy_function=copy2)
- 递归移动文件、目录到目标,返回目标
- 本身使用的是 os.rename方法
- 如果不支持rename,如果是目录则copytree再删除源目录
- 默认使用copy2方法
os.rename('/etc/bin/t.txt', '/etc/temp/t')
os.rename('test3', '/etc/temp/test300')
12
- shutil还有打包功能。生成tar并亚索。支持zip、gz、bz、xz
csv文件
csv文件简介
- 参看 RFC 4180
http://www.ietf.org/rfc/rfc4180.txt - 逗号分隔值Comma-Separated Values
- CSV 是一个被行分隔符、列分隔符划分成行和列的文本文件
- CSV不指定字符编码
- 行分隔符为\r\n,最后一行可以没有换行符
- 列分隔符常为逗号或者制表符
- 每一行成为一条记录record
- 字段可以使用双引号括起来,也可以不使用。如果字段中出现了双引号、逗号、换行符必须使用双引号括起来。如果字段的值是双引号,使用两个双引号表示一个转义
- 表头可选,和字段列对齐就行
手动生成csv文件
from path impoet Path
p = Path('/etc/python/test.csv')
parent = p.parent
if not parent.exists():
parent.mkdir(parents=True)
csv_body = '''\
id,name,age,comment
1,zs,18,:I'm 18"
2,ls,20,"this is a ""test"" string."
3,ww,23,"你好
计算机
"
'''
p.write_text(csv_body)
1234567891011121314151617
csv 模块
reader(csvfile, dialect=‘excel’, **fmtparams)
- 返回reader 对象,是一个行迭代器
- 默认使用excel方言,如下:
- delimiter 列分隔符,逗号
- lineterminator 行分隔符\r\n
- quotechar 字段的引用符号,缺省为 " 双引号
- 双引号的处理
- doublequote 双引号的处理,默认为True。如果碰到数据中有双引号,而quotechar也是双引号,True则使用2个双引号表示,False表示使用转义字符将作为双引号的前缀
- escapechar 一个转义字符,默认为None
- writer = csv.writer(f, doublequote=False, escapechar=’@’)遇到双引号,则必须提供转义字符
- quoting 指定双引号的规则
- QUOTE_ALL 所有字段
- QUOTE_MINIMAL 特殊字符字段,Excel方言使用该规则
- QUOTE_NONNUMERIC 非数字字段
- QUOTE_NONE 都不使用引号
writet(csvfile, dialect=‘excel’, **fmtparams)
- 返回DictWriter的实例
- 主要方法有writerow、writerows
writerow(iterable)
import csv
from pathlib import Path
p = Path('c:/tmp/mycsv/test.csv')
with open(str(p)) as f:
reader = csv.reaber(f)
print(next(reader))
print(next(reader))
for line in reader:
print(line)
rows = [
[4,'tom',22,'tom'],
(5,'jerry',24,'jerry'),
(6,'justin',22,'just\t"in'),
"abcdefgh",
((1,),(2,))
]
row = rows[0]
with open(str(p), 'a') as f:
writer = csv.writer(f)
writer.writerow(row)
writer.writerows(rows)
123456789101112131415161718192021222324
- 说明row行,需要一个可迭代就可以,可迭代的每一个元素,将作为csv行的每一个元素
- windows下回再每行末尾多出一个/r,结果办法 open(‘test.csv’, ‘w’, newline=’’)
ini文件处理
- 作为配置文件,ini文件的格式很流畅

- 中括号里面的部分称为section,译作节、区、段
- 每一个section内,都是key=value形成的键值对,key称为option选项
- 注意这里的DEFAULT是缺省section的名字,必须大写
configparser
- configparser模块的ConfigParser类就是用来操作
- 可以将section当做key,section存储着键值对组成的字典,可以把ini配置文件当做一个嵌套的字典。默认使用的是有序字典
read(filenames, encoding=None)
- 读取ini文件,可以是单个文件,也可以是文件列表。可以指定文件编码
sections()
- 返回section列表。缺省section不包括在内
add_section(section_name)
- 增加一个section
has_section(section, option)
- 判断seciton是否存在
options(section)
- 返回section的所有option,会追加缺省section的option
has_option(section, option)
- 判断section是否存在这个option
get(section, option, *, raw=False, vars=None[, fallback])
- 从指定的段的选项上取值,如果找到返回,如果没有找到就去找DEFAULT段有没有
getint(section, option, *, raw=False, vars=None[, fallback])
getfloat(section, option, *, raw=False,vars=None[, fallback])
getboolean(section, option, *, raw=False,vars=None[, fallback])
上面3个方法和get一样,返回指定类型数据
items(raw=False, vars=None)
items(section, raw=Fale, vars=None)
- 没有section,则返回所有section名字及其对象;如果指定section,则返回这个指定的seciton的键值对组成而元组
set(section, option, value)
- section存在的情况下,写入option=value,要求option、value必须是字符串
remove_section(section)
- 移除seciton及其所有option
remove_option(section, option)
- 移除section下的option
write(fileobject, space_aroud_delimiters=True)
- 将当前config的所有内容写入fileobject中,一般open函数使用w模式
from configparser import ConfigParser
filename = 'test.ini'
newfilename = 'mysql.ini'
cfg = ConfigParser()
read_ok = cfg.read(filename)
print(read_ok)
# print(cfg.sections())
print(cfg.has_section('client'))
for k,v in cfg.items(): # 未指定section
print(k, type(k))
print(v, type(v))
print(cfg.items(k))
print()
print('-'*30)
for k,v in cfg.items("mysqld"): # 指定section
print(k, type(k))
print(v, type(v))
print('~~~~~~~')
print('-'*30)
# 取值
tmp = cfg.get('mysqld', 'port')
print(type(tmp), tmp)
tmp = cfg.get('mysqld', 'a')
print(type(tmp), tmp)
print(cfg.get('mysqld', 'hello', fallback='python')) # 缺省值
# 按照类型
tmp = cfg.getint('mysqld', 'port')
print(type(tmp), tmp)
if cfg.has_section('test'):
cfg.remove_section('test')
cfg.add_section('test')
cfg.set('test', 'test1', '1')
cfg.set('test', 'test2', '2')
with open(newfilename, 'w') as f:
cfg.write(f)
print(cfg.getint('test', 'test2'))
cfg.remove_option('test', 'test2')
# 字典操作更简单
cfg['test']['x'] = '1000' # key不存在
cfg['test2'] = {'test2':'1000'} # section不存在
print('x' in cfg['test'])
print('x' in cfg['test2'])
# 其他内容方式
print(cfg._dict) # 返回默认的字典类型,默认使用有序字典<<class 'collections.OrderedDict>>
for k,v in cfg._sections.items():
print(k,v)
for k,v in cfg._sections['mysqld'].items():
print(k,v)
# 修改后需再次写入
with open(newfilename, 'w') as f:
cfg.write(f)
为什么要序列化
- 内存中的字典、列表、集合以及各种对象,如何保存到一个文件中?
- 如果是自己定义的类的实例,如何保存到一个文件中?
- 如何从文件中读取数据,并让他们在内存中再次恢复成自己对应的类的实例?
- 要设一套协议,就按照某种规则,把内存中数据保存到文件中。文件是一个字节序列,所以必须把数据转换成字节序列,输出到文件。这就是序列化。反之,把文件的字节序列恢复到内存并且还是原来的类型,就是反序列化
定义
serialization 序列化
- 将内存中对象存储下来,把它变成一个个字节 ->二进制
deserialization 反序列化
- 将文件的一个个字节恢复成内存中对象 -> 二进制
- 序列化保存到文件就是持久化
- 可以将数据序列化后持久化,或者网络传输;也可以将文件中或者网络接收到的字节序列反序列化
- Python提供了pickle库
pickle库
- Python中的序列化、反序列化模块

import pickle
filename = 'o:/ser'
i = 99
c = 'c'
l = list('123')
d = {'a':1, 'b':'abc', 'c':[1,2,3]}
# 序列化
with open(filename, 'wb') as f:
pickle.dump(i, f)
pickle.dump(c, f)
pickle.dump(l, f)
pickle.dump(d, f)
# 反序列化
with open(filename, 'rb') as f:
print(f.read(), f.seek(0))
for i in range(4)
x =pickle.load(f)
print(x, type(x))
12345678910111213141516171819202122
- 对象序列化
import pickle
class AAA:
tttt = 'ABC'
def show(self):
print('abc')
a1 = AAA() # 创建AAA类的对象
# 序列化
ser = pickle.dumps(a1)
print('ser={}'.format(ser)) # AAA
# 反序列化
a2 = pickle.loads(ser)
print(a2.tttt)
a2.show()
1234567891011121314151617
- 上面的例子中,故意使用了连续的AAA、ABC、abc等字符串,就是为了在二进制文件中能容易的发现它们
- 上例中,其实就保存了一个类名,因为所有的其他东西都是类定义的东西,是不变的,所以只序列化一个AAA类名。反序列化的时候找到类就可以恢复一个对象
import pickle
# 对象序列化
class AAA:
def__int__(self):
self.aaaa = 'abc'
a1 = AAA() # 创建AAA类的对象
# 序列化
ser = pickle.dumps(a1)
print('ser={}.format(ser)) # AAA aaaa abc
# 反序列化
a2 = pickle.loads(ser)
print(a2, type(a2))
print(a2.aaaa)
print(id(a1), id(a2))
123456789101112131415161718
- 可以看出这回除了必须保存的AAA,还序列化了aaaa和abc,因为这是每一个对象自己的属性,每一个对象不一样的,所以这些数据需要序列化
序列化、反序列化实验
- 定义类AAA,并序列化到文件
import pickle
# 实验
# 对象序列化
class AAA:
def __init__(self):
self.aaaa = 'abc'
a1 = AAA() # 创建AAA类的对象
# 序列化
ser = pick.dumps(a1)
print('ser={}'.format(ser)) # AAA aaaa abc
print(len(ser))
filename = 'o:/ser'
with open(filename, 'wb') as f:
pickle.dump(a1, f)
12345678910111213141516171819
- 将产生的序列化文件ser发送到其他节点上
- 增加一个x.py文件,内容如下。最后执行这个脚本 $ python x.py
import pickle
with open('ser', 'rb') as f:
a = pickle.load(f) # 异常
1234
- 会抛出异常**AttributeError: Can’t get attribute ‘AAA’ on <module ‘main’ from ‘t.py’>
- 这个异常实际上是找不到类AAA的定义,增加类定义即可解决
- 反序列化的时候找到AAA类的定义,才能成功。否则就会抛出异常
- 可以这样理解:反序列化的时候,类是模子,二进制序列就是铁水
import pickle
class AAA:
def show(self):
print('xyz')
with open('ser', 'rb') as f:
a = pickle.load(f)
print(a)
a.show()
print(a.aaaa)
1234567891011
- 这里定义了类AAA,并且上面的代码也能成功的执行
- 注意:
这里的AAA定义和原来完全不同了
因此,序列化、反序列化必须保证使用同一套类的定义,否则会带来不可预料的结果
序列化应用
一般来说,本地序列化的情况,应用较少。大多数场景都应用在网络传输中。
将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接受到的数据反序列化后,就可以使用了
但是需要注意,远程接受端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类,必须远程得有一致的定义
现在,大多数项目,都不是单机的,也不是单服务的,需要多个程序之间配合。需要通过网络将数据传送到其他节点上去,这就需要大量的序列化、反序列化过程
但是,问题是,Python程序之间还可以都用pickle解决序列化、反序列化,如果是跨平台、跨语言、跨协议。pickle就不太适合,需要公共的协议。例如XML、Json、Protocol Buffer等
不同的协议,效率不同,适用不同的场景,根据不同的情况分析选型
Json
Json(JavaScript Object Notation,JS 对象标记)是一种轻量级的数据交换格式。它基于 ECMAScript(w3c组织制定的JS规范)的一个子集,采用完全独立于变成语言的文本格式来存储和表示数据
Json官网:http://json.org/
Json是数据类型
值
- 双引号引起来的字符串,数值,true和false,null,对象,数组。这些都是值

- 字符串:由双引号包围起来的任意字符的组合,可以有转义字符
- 数值:有正负,有整数、浮点数
- 对象:无序的键值对的集合
格式:{key1:value1,…,keyn:valuen}
key必须是一个字符串,需要双引号包围这个字符串
value可以是任意合法的值

- 数组:有序的值的集合
格式:[val1,…,valn]
- 实例
{
"person":[
{
"name": "tom",
"age": 18
},
{
"name": "jerry",
"age": 16
}
],
"total": 2
}
12345678910111213
Json 模块
Python 与 Json
- Python支持少量内建数据类型到Json类型的转换

常用方法

import json
d = {'name':'Tom', 'age':20, 'interest':('music', 'movie'), 'class':['python']}
j = json.dumps(d)
print(j, type(j)) # 注意引号、括号的变化,注意数据类型的变化
d1 = json.loads(j)
print(d1)
print(id(d), id(d1))
12345678
一般json编码的数据很少落地,数据都是通过网络传输。传输的时候,要考虑压缩它
本质上来说它就是个文本,就是个字符串
json很简单,几乎编程语言都支持json,所以应用范围十分广泛
MessagePack
官网:https://msgpack.org/
MessagePack是一个基于二进制高效的对象序列化类库,可用于跨语言通信
它可以像Json那样,在许多语言之间交换结构对象
但是它比Json更快速也更轻巧
支持Python、Ruby、Java、C/C++等众多语言。
兼容json和pickle
# Json:27 bytes
{"person":[{"name":"tom","age":18},{"name":"jerry",."age":16}],"total":2}
# MessagePack:18 bytes
# 82 a7 63 6f 6d 70 61 63 74 c3 a6 73 63 68 65 6d 61 00
12345
可以看出,大大的节约了空间
安装
$ pip install msgpack
1
常用方法
- packb 序列化对象。提供了dumps来兼容pickle和json
- unpackb 反序列化对象。提供了loads来兼容
- pack 序列化对象保存到文件对象。提供了dump来兼容
- unpack 反序列化对象保存到文件对象。提供了load来兼容
import pickle
import msgpack
import json
d = {'person': [{'name': 'tom', 'age': 18}, {'name': 'jerry', 'age': 16}], 'total':2}
# json
data = json.dumps(d)
print(type(data), len(data), data)
print(data.replace(' ', '')
print(len(data.replace(' ', ''))) # 72 bytes 注意这样替换的压缩是不对的
# pickle
data = pickle.dumps(d)
print(type(data), len(data), data) # 101 bytes
# msgpack
data = msgpack.dumps(d)
print(type(data), len(data), data) # 48 bytes
print('-' * 30)
u = msgpack.umpackb(data)
print(typr(u), u)
print(msgpack.loads(data))
u = msgpack.loads(data, encoding='utf-8')
print(type(u), u)
12345678910111213141516171819202122232425262728
- MessagePack简单易用,高效压缩,支持语言丰富
- 所以,用它序列化也是一种很好的选择。Python很多大名鼎鼎的库都是用了msgpack
概述
- 正则表达式,Regular Expression,缩写为regex、regexp、RE等。
- 正则表达式是文本处理极为重要的技术,用它可以对字符串按照某种规则进行检索、替换。
- 1970年代,Unix之父Ken Thompson将正则表达式引入到Unix中文本编辑器ed和grep命令中,由此正则表达式普及开来。
- 1980年后,perl语言对Henry Spencer编写的库,扩展了很多新的特性。1997年开始,Philip Hazel开发出了PCRE(Perl Compatible Regular Expressions),它被PHP和HTTPD等工具采用。
- 正则表达式应用极其广泛,shell中处理文本的命令、各种高级编程语言都支持正则表达式。
- 参考:https://www.w3cschool.cn/regex_rmjc/
分类
BRE
- 基本正则表达式,grep、sed、vi等软件支持。vim有扩展
ERE
- 扩展正则表达式,egrep(gerp -E)、sed -r等
PCRE
- 几乎所有高级语言都是PCRE的方言或者变种,Python从1.6开始就使用SRE正则表达式引擎,可以认为是PCRE的子集,见模块re
基本语法
元字符 metacharacter

转义
- 凡是在正则表达式中有特殊意义的符号,如果想使用它的本意,需使用\转义
- 反斜杠自身,得使用\
- \r,\n代表回车、换行
重复

练习
- 1、匹配手机号码
字符串为“手机号码13899879021” - 2、匹配中国坐骑
字符串为“号码027-65666837、0543-5837291” - 代码实现
- 1、\d{11}
- 2、\d{3,5}-\d{7,8}
or匹配与分组

- 注意:
分组和捕获是同一个意思
使用正则表达式,能用简单表达式,就不要复杂的表达式
零宽度断言

- 注意:
断言不占分组号。断言如同条件,只是要求匹配必须满足断言的条件
注释

贪婪与非贪婪
- 默认是贪婪模式,也就是说尽量多匹配更长的字符串
- 非贪婪很简单,在重复的符号后面加上一个?问号,就尽量的少匹配了


引擎选项

工作模式
单行模式
- . 点号可以匹配所有字符,包括换行符
- ^ 表示整个字符串的开头
- $ 表示整个字符串的结尾
多行模式
- . 点号可以匹配除了换行符之外的字符,多行不影响 . 点号
- ^ 表示行首,$ 表示行尾,不过这里的行是每一行
默认模式
- 可以看做待匹配的文本是一行,不能看做多行
- . 点号不能匹配换行符
- ^ 和 $ 表示行首和行尾,而行首行尾就是整个字符串的开头和结尾
总结
- 简单讲,单行模式只影响 . 点号行为,多行模式重新定义行影响了 ^ 和 $
- 字符串看不见的换行符,\r\n会影响e的 测 试 , e 的测试,e的测试,e 只能匹配e\n
举例
very very happy
harry key
- 上面2行happy之后,有可能是\r\n结尾
- y$ 单行匹配key的y,多行匹配happy和key的y(这里的匹配是全局匹配)
- .$ 指的是此行的结尾,而默认模式和单行模式都是一行,指的是这个大字符串的最后一个字符,就是key的y
Python使用re模块提供了正则表达式处理的能力
常量

多重选项
- 使用 | 位或 运算开启多重选项
- 如果单独存在,设计为1-9 如果多个存在,设置为1,2,4,8,16…
impoet re
re.M | re.S
123
方法
编译
re.compile(pattern, flags=0)
1
- 设定flags,编译模式,返回正则表达式对象regex
- pattern就是正则表达式字符串,flags是选项,指代当前工作模式。正则表达式需要被编译,为了提高效率,这些编译后的结果被保存,下次使用同样的pattern的时候,就不需要再次编译
- re的其他方法为了提高效率都调用了编译方法,就是为了提速
# 先编译在操作
import re
s = 'apple\nbig'
regex = re.compile('^a', re.M)
r = regex.match(s)
print(s)
12345678
单次匹配
match
re.match(pattern, string, flags=0)
regex.match(string[, pos[, endpos]])
12
- match 匹配,只从开头匹配
- regex对象match方法可以重设定开始位置和结束位置。返回match对象
search
re.search(pattern, string, flags=0)
regex.search(string[, pos[, endpos]])
12
- 从头搜索直到第一个匹配
- regex对象search方法可以重设定开始位置和结束位置,返回match对象
fullmatch
re.fullmatch(pattern, string, flags=0)
regex.fullmatch(string[, pos[, endpos]])
12
- 全长完全匹配,整个字符串和正则表达式匹配
match举例
import re
s = """python\nhello\nwho"""
r = re.match('p', s)
print(type(r), r) # match 对象,出一个结果
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 1), match='p'>
12345678
# 设置模式
import re
s = """python\nhello\nwho"""
r = re.match('^h', s, re.M)
print(type(r), r) # match 对象,出一个结果
# 打印结果
<class 'NoneType'> None
# None原因:虽然re.M为多行模式,但是match只从头开始找
1234567891011
# 先编译,在匹配,设置开始位置
import re
s = """python\nhello\nwho"""
regex = re.compile('t', re.M)
r = regex.match(s, 2) # 把索引2作为开始找
print(type(r), r)
#打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(2, 3), match='t'>
# 只有先编译,在match,才可以调整开始位置
1234567891011
- 总结:mauch不管单行多行,只从头或指定开始索引找
search举例
import re
s = """python\nhello\nwho"""
#regex = re.compile('h', re.M)
r = re.search('h',s) # 找到python的h就停止匹配了
print(type(r), r)
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(3, 4), match='h'>
123456789
# 设置模式
import re
s = """python\nhello\nwho"""
#regex = re.compile('h', re.M)
r = re.search('e',s, re.M)
print(type(r), r)
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(8, 9), match='e'>
1234567891011
# 先编译在匹配,设置开始位置
import re
s = """python\nhello\nwho"""
regex = re.compile('h', re.M) # 设置为多行模式
r = regex.search(s,4,9) # 从索引4开始,到索引8结束,[4,9)
print(type(r), r)
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 8), match='h'>
1234567891011
- 总结: search不管是不是多行,找到就返回
fullmatch举例
import re
s = """python\nhello\nwho"""
regex = re.compile('.+', re.S) # .+ 在单行模式下, . 点可匹配到换行符
r = regex.search(s)
print(type(r), r) # match='python\nhello\nwho'
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 16), match='python\nhello\nwho'>
123456789
import re
s = """python\nhello\nwho"""
regex = re.compile('\w+') # 先编译
r = regex.search(s, 1, 3) # 匹配 [1,3)
print(type(r), r) # match =yt
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(1, 3), match='yt'>
123456789
- 总结:fullmatch不管单行多行模式情况下整个字符串(或指定区间)需与正则表达式匹配
全文搜索
findall
re.findall(pattern,string,flags=0)
regex.findall(string[, pos[, endpos]])
12
- 对整个字符串,从左至右匹配,返回所有匹配项的列表,里面是str
finditer
re.finditer(pattern,string,flags=0)
regex.finditer(string[, pos[, endpos]])
12
- 对整个字符串,从左至右匹配,返回所有匹配项,返回迭代器
- 注意每次迭代返回的是match对象
findall举例
import re
s = """python\nhello\nwho"""
r= re.findall('h',s)
print(r)
# 打印结果
['h', 'h', 'h']
12345678
# 按区间匹配,需先编译
import re
s = """python\nhello\nwho"""
regex = re.compile('h')
r= regex.findall(s, 3,10) # 匹配 [3,10)
print(r)
# 打印结果
['h', 'h']
1234567891011
finditer举例
import re
s = """python\nhello\nwho"""
r= re.finditer('h',s)
print(r) # <callable_iterator object at 0x00000000021E09B0>
for i in r:
print(type(i),i,s[i.start():i.end()]) # s[i.start():i.end()] 切片拿到match值
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(3, 4), match='h'> h
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 8), match='h'> h
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(14, 15), match='h'> h
123456789101112
匹配替换
sub
re.sub(pattern, replacement, string, count=0, flags=0)
regex.sub(replacement, string, count=0)
12
- 使用pattern对字符串string进行匹配,对匹配项使用replacement替换,返回的是str
- replacement可是是string、bytes、function
subn
re.subn(pattern, replacement, string, count=0, flags=0)
regex.subn(replacement, string, count=0)
12
- 同sub返回一个元祖(new_string, number_of_subs_made)
sub举例

import re
s = """python\nhello\nwho"""
r= re.sub('h', 'ab', s)
print(type(r), r)
# 打印结果
<class 'str'> pytabon
abello
wabo
12345678910
# 指定替换次数
import re
s = """python\nhello\nwho"""
r= re.sub('h', 'ab', s, 1) # 替换1次
print(type(r), r)
# 打印结果 # 仅python被替换为pytabon
<class 'str'> pytabon
hello
who
1234567891011
# 先编译后替换,指定替换次数
import re
s = """python\nhello\nwho"""
regex = re.compile('h')
r= regex.sub('ab', s, 2) # 替换2次
print(type(r), r)
# 打印结果 # 仅python和hello被替换
<class 'str'> pytabon
abello
who
123456789101112
- 引用分组,添加后缀或前缀
# 添加前缀
import re
s = """honey\nhello\nhi"""
r= re.sub('(h\w+)', r'python-----\1', s)
print(r)
# 打印结果
python-----honey
python-----hello
python-----hi
# 添加后缀
r= re.sub('(h\w+)', r'\1------python', s)
print(r)
# 打印结果
honey------python
hello------python
hi------python
1234567891011121314151617181920
sunb举例
import re
s = """honey\nhello\nhi"""
r= re.subn('h', 'p', s)
print(type(r), r)
# 打印结果
<class 'tuple'> ('poney\npello\npi', 3)
for i in r:
print(i)
# 打印结果
poney
pello
pi
3
1234567891011121314151617
分割字符串
re.split(pattern, string, maxsplit=0, flags=0)
1
- re.split 分割字符串
import re
s = """
os.path.abspath(path)
normpath(join(os.getcwd(), path))
"""
# 把每行单词提取出来
print(s.split()) # 做不到
# 打印结果
['os.path.abspath(path)', 'normpath(join(os.getcwd(),', 'path))']
print(re.split('[\.()\s,]+', s))
# 打印结果
['', 'os', 'path', 'abspath', 'path', 'normpath', 'join', 'os', 'getcwd', 'path', '']
123456789101112131415
分组
- 使用小括号的pattern捕获的数据被放到了组group中
- match、search函数可以返回match对象
- findall返回字符穿列表;finditer返回一个个match对象
group()
- 如果pattern中使用了分组,如果有匹配的结果,会在match对象中
- 使用group(N)方式返回对应分组,1到N是对应的分组,0返回整个匹配的字符串,N不写缺省为0
- 如果使用了命名分组,可以使用group(‘name’)的方式取分组
- 也可以使用groups()返回所有组
- 使用groupdict()返回所有命名的分组
分组
import re
s = '''bottle\nbag\nbig\napple'''
regex = re.compile('(b\w+)') # 先编译
result = regex.match(s) # 从头开始匹配一次
print(type(result)) # <class '_sre.SRE_Match'>
print(result.group()) # bottle
12345678
命名分组
- 分组命名从1开始,0代表整个match对象
import re
s = '''bottle\nbag\nbig\napple'''
regex = re.compile('(b\w+)\n(?P<name2>b\w+)\n(?P<name3>b\w+)')
result = regex.match(s)
print(result) # type(result)返回的是 <class '_sre.SRE_Match'>
# 打印结果 <_sre.SRE_Match object; span=(0, 14), match='bottle\nbag\nbig'>
print(result.group(1),result.group(2),result.group(3)) # 通过分组索引取对应分组值
# 打印结果 bottle bag big
print(result.group('name2'),result.group('name3')) # 通过命名分组名称取对应分组值
# 打印结果 bag big
print(result.groupdict()) # 将命名分组组成kv对放入字典
# 打印结果 {'name2': 'bag', 'name3': 'big'}
print(result.group(0)) # 等效result.group()
# 打印结果
bottle
bag
big
123456789101112131415161718192021222324
- findall用法
import re
s = '''bottle\nbag\nbig\napple'''
regex = re.compile('(b\w+)\n(?P<name2>b\w+)\n(?P<name3>b\w+)')
result = regex.findall(s)
for x in result:
print(type(x),x)
# 打印结果
<class 'tuple'> ('bottle', 'bag', 'big')
1234567891011
- finditer用法
import re
s = '''bottle\nbag\nbig\napple'''
regex = re.compile('(?P<head>b\w+)')
result = regex.finditer(s)
for x in result:
print(type(x), x, x.group(), x.group('head'))
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 6), match='bottle'> bottle bottle
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 10), match='bag'> bag bag
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(11, 14), match='big'> big big