文件操作:文件的打开关闭和读写
上下文管理 with
路径操作os.path模块、pathlib模块
os模块
文件操作
打开文件
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
- 基本操作
f = open("test") # file对象
# windows <_io.TextIOWrapper name='test' mode='r' encoding='cp936'>
# linux <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'>
print(f.read()) # 读取文件
f.close() # 关闭文件
windows 中使用 codepage 代码页,可以认为每一个代码页就是一张编码表。cp936 等同于 GBK。
open 的参数
- mode 模式
- r 模式
- 默认打开模式
- 只读打开文件,如果使用 write 方法,会抛异常。
- 如果文件不存在,抛出 FileNotFoundError 异常
- w 模式
- 表示只写方式打开,如果读取则抛出异常
- 如果文件不存在,则直接创建文件
- 如果文件存在,则清空文件内容,即会用空文件覆盖之前的文件
- x 模式
- 文件不存在,创建文件,并只写方式打开
- 文件存在,抛出 FileExistsError 异常
- 即要求文件不存在
- a 模式
- 文件存在,只写打开,追加内容
- 文件不存在,则创建后,只写打开,追加内容
+
模式- 为 r、w、a、x 提供缺失的读或写功能,但是,获取文件对象依旧按照 r、w、a、x 自己的特征
+
模式不能单独使用,可以认为它是为前面的模式字符做增强功能的
总结:
- r 是只读,wxa 都是只写
- wxa 模式都可以产生新文件
- w 不管文件是否存在,都会生成全新的内容文件
- a 不管文件是否存在,都能在打开的文件尾部追加
- x 必须要求文件不存在,自己造一个新的文件
-
文本模式 t
- 字符流,将文件的字节按照某种字符编码理解,按照字符操作
- open 的默认缺省模式就是 rt
-
二进制模式 b
- 字节流,将文件按照字节理解,与字符编码无关
- 二进制模式操作时,字节操作使用 bytes 类型
-
其他参数
待补充
文件指针
- 文件指针,指向当前字节位置
- r 模式,指针起始位置为 0,a 模式指针起始位置为 EOF
tell
- 显示指针当前位置
seek
- seek(offset[,whence]) 移动文件指针位置
- offset 为偏移字节数
- 可以超界,当向前 seek 时,不能超界,否则抛异常
- whence 表示从哪里开始偏移
- 0 缺省值,表示相对于开头开始偏移
- 1 表示相对于当前位置偏移
- 2 表示相对于 EOF 位置偏移
- 文本模式下
- whence 是 0 时,offset 只能是正整数,seek(n) 即 seek(n,0),n 可超右边界
- whence 是 1 时,offset 只能是 0 即只能使用 seek(0,1) 文件指针不动
- whence 是 2 时,offset 只能是 0,即 seek(0,2) 把文件指针移到末尾
- 二进制模式下
- whence 是 0 时,offset 只能是正整数
- whence 是 1 时,offset 可正可负
- whence 是 2 时,offset 可正可负
文件读写
read
- read(size=-1)
- size 表示读取的多少个字符(文本模式)或者字节(二进制模式)
- size 为负数或者 None 表示从当前文件指针位置读取到 EOF
# 在工作目录创建文件并写入123456789
f = open('test','rt')
print(f.tell()) # 文件开头
print(f.read(1)) # 读一个字符
print(f.read()) # 从当前文件指针读到末尾
print(f.tell()) # 当前文件指针在末尾
print(f.seek(0)) # 移动文件指针到开头
print(f.read(3)) # 读三个字符
print(f.tell())
print(f.seek(0,2)) # 移动文件指针到末尾
print(f.read()) # 空
f.close()
-----------------------
0
1
23456789
9
0
123
3
9
readline
- readline(size=-1), 一行行读取文件内容
- size 设置一次能读取行内几个字符或者字节
readlines
- readlines(hint=-1), 读取所有行的列表
- 指定 hint 则返回指定的行数
f = open('test')
print(f.readline(),end='')
print(f.readlines())
-------------------------
a
['b\n', 'c\n', 'd']
# 推荐做法
f = open('test') # f就是可迭代对象
for line in f:
print(line,end='')
f.close()
----------------------
a
b
c
d
# 惰性方式
def rf():
yield from open('test')
for i in rf():
print(i, end='')
write
- write(s), 把字符串 s 写入到文件中
- 并返回字符的个数
writelines
- writelines(lines), 将字符串列表写入文件
f = open('test','w+')
lines = ['a','b\n','c','d'] # 这种方式要想换行,要自己加
f.writelines(lines)
f.close()
print(open('test').read())
---------------------------
ab
cd
f = open('test','w+')
lines = ['a','b','c','d']
f.write('\n'.join(lines))
f.seek(0)
print(f.read())
f.close()
--------------------------
a
b
c
d
关闭文件
close
- flush 并关闭文件对象
- 文件已经关闭,再次关闭无影响,可多次关闭
其他
- seekable() 是否可以 seek
- readable() 是否可读
- writeable() 是否可写
- closed 是否已经关闭 f.closed
一种特殊的语法,交给解释器去释放文件对象
上下文管理with
- 使用 with...as 关键字
- 上下文管理的语句块并不会开启新的作用域,函数 / 类 / 模块中有作用域的概念
- with 语句块执行完的时候,会自动关闭文件对象
- 打开文件由 open 函数完成,跟 with 无关
with open('test') as f: # 上下文管理:进入时f = open('test').__enter__()——> 离开时关闭文件对象
print(f.read(),f.closed)
print('~'*20)
print(f.closed)
---------------------------
False
~~~~~~~~~~~~~~~~~
True
- 无论 with 语句块中是否中有异常,都能关闭文件对象
# 可以在jupyter中运行
with open('test') as f:
print(f.read(),f.closed)
1/0
print('*********************')
print(f.closed)
# print(f.closed)
--------------------------------
False
Traceback (most recent call last):
1/0
ZeroDivisionError: division by zero
# True
- 深入理解上下文管理 with...as 参看python学习笔记:上下文管理
路径操作
os.path模块
py3.4之前使用
- 路径拼接
os.path.join
- 路径存在
os.path.exists
- 路径分割
os.path.split
- 路径
os.path.dirname
和路基os.path.basename
- 标准路径
os.path.realpath
:返回指定文件的标准路径,真实路径,如果为软链接,返回指向的路径 - 绝对路径
os.path.abspath
:返回一个目录的绝对路径
import os
p = os.path.join('/usr', 'local', 'bin/python3') # 拼接路径
print(type(p), p) # str
print(os.path.split(p)) # ('/usr/local/bin', 'python3')
print(os.path.exists(p)) # 是否存在
print(os.path.realpath(p)) # 真实路径
print(os.path.abspath(p)) # 绝对路径,注意于realpath的区别
print(os.path.basename(p)) # 路基,最后一级
print(os.path.dirname(p)) # 路径,不包含最后一级
THIS_FILE_PATH = os.path.dirname(os.path.realpath(__file__)) # 当前文件路径
print(THIS_FILE_PATH)
- 判断文件或目录
os.path.isfile
os.path.isdir
os模块
- 获取操作系统平台
os.name
:windows 返回nt,linux返回posixos.uname
:*nix
平台支持sys.platform
:win返回win32,linux返回linux
- 常用
os.getcwd()
:获取当前工作目录os.listdir()
:返回指定目录下的所有文件和目录名os.walk()
:目录遍历器,在目录树中游走输出在目录中的文件名,参看os.walk
# 找出文件夹下所有的py文件
import os
def find_py_files(folder):
py_files = []
for root, dirs, files in os.walk(folder):
for file in files:
if file.endswith('.py'):
py_files.append(os.path.join(root, file))
return py_files
# Example usage
folder = '/path/to/folder'
py_files = find_py_files(folder)
print(py_files)
os.remove()
:删除一个文件
-os.system()
:运行shell命令
-os.mkdir()
:生成单级目录
-os.makedirs()
:生成多层递归目录
-os.rmdir()
和os.removedirs()
:删除空目录
-os.rename()
:重命名文件或目录
- 其他
os.stat(path, *, dir_fd=None, follow_symlinks=True)
os.chmod(path, mode, *, dir_fd=None, follow_symlinks=True)
os.chown(path, uid, gid)
pathlib模块
>=py3.4
建议使用,提供Path对象来操作,包含目录和文件
初始化
from pathlib import Path
p = Path() # 或 Path('.') 或 Path('') 表示当前目录
p1 = Path('a', 'b', 'c/d') # 当前目录下的a/b/c/d
p2 = Path('/etc') # 根目录下的etc目录
路径拼接和分解
/
和joinpath
- Path对象
/
Path对象、Path对象/
字符串 或者 字符串/
Path对象 - 不能字符串
/
字符串 - joinpath:joinpath(*other) 连接多个字符串到Path对象中
- Path对象
from pathlib import Path
p = Path()
p = p / 'a'
p1 = 'b' / p
p2 = Path('c')
p3 = p2 / p1
# p4 = 'e' / 'f' / p3 # TypeError: unsupported operand type(s) for /: 'str' and 'str'
p5 = p3 / 'e' / 'f'
print(p5)
print(p5.parts)
p6 = p5.joinpath('etc', 'init.d', Path('httpd'))
print(p6)
注意:p~p6这里并不是字符串,而是Path类实例,可查看type(p6)
- parts属性
- 返回元组
- 返回的是路径中的每一个部分,见上例
绝对路径
- resolve():当前Path对象的绝对路径,如果是软链接则直接被解析
- absolute():获取绝对路径
获取路径
- str 获取路径字符串
- bytes获取路径字符串的bytes
p = Path("/usr/local/bin/python3")
print(type(p))
print(str(p))
print(bytes(p))
print(p.resolve())
print(p.absolute())
------------------
<class 'pathlib.PosixPath'>
/usr/local/bin/python3
b'/usr/local/bin/python3'
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3.8
/usr/local/bin/python3
父目录
- parent 目录的逻辑父目录
- parents 父目录序列
- 返回可迭代对象
- 索引0是直接的父,即parent
p = Path('/a/b/c/d')
print(p.parent.parent)
print(p.parent)
print(p.parents[0])
for x in p.parents:
print(x)
# print(p.parents[-1]) # raise IndexError(idx)
print(p.parents[len(p.parents)-1])
全局方法
- cwd() 返回当前工作目录
- home() 返回当前家目录
p = Path('a/b')
print(Path.cwd())
print(p.home())
目录组成部分
name、stem、suffix、suffixes、with_suffix(suffix)、with_name(name)
- name 目录的最后一个部分
- suffix 目录中最后一个部分的扩展名
- stem 目录最后一个部分,没有后缀
- name = stem + suffix
- suffixes 返回多个扩展名列表
- with_suffix(suffix) 有扩展名则替换,无则补充扩展名
- with_name(name) 替换目录最后一个部分并返回一个新的路径
p = Path('/monkey/mysqlinstall/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('mysql-5.tgz')) # 替换最后一部分,返回新的路径/monkey/mysqlinstall/mysql-5.tgz
print(p.with_suffix('.png')) # 有扩展名则替换/monkey/mysqlinstall/mysql.tar.png
p = Path('README')
print(p.with_suffix('.txt')) # 无则补充扩展名,README.txt
判断方法
- exists() 目录或文件是否存在
- is_dir() 是否是目录,目录存在返回True
- is_file() 是否是普通文件,文件存在返回True
- is_symlink() 是否是软链接
- is_socket() 是否是socket文件
- is_block_device() 是否是块设备
- is_char_device() 是否是字符设备
- is_absolute() 是否是绝对路径
p = Path("/usr/local/bin/python3")
print(p.exists())
print(p.is_file())
print(p.is_dir())
print(p.is_symlink())
迭代目录
- iterdir():迭代当前目录,不递归
如下目录结构:
1
├── 1.py
├── 2
│ └── test.py
├── 2.py
└── 3
p = Path('1')
print(p.iterdir(), type(p.iterdir()))
for i in p.iterdir():
print(i)
操作
- rmdir() : 删除空目录,当前Path未提供目录判空方法,可用iterdir来判断
- touch(mode=0o666, exist_ok=True) :创建一个文件
- as_uri() :将路径返回成URI,例如'file:///etc/passwd'
- mkdir(mode=0o777, parents=False, exist_ok=False)
parents,是否创建父目录,为True等同于shell命令
mkdir -p
。False时,父目录不存在,则抛出FileNotFoundError
exist_ok参数,在3.5版本加入。False时,路径存在,抛出FileExistsError;True时,FileExistsError被忽略
from pathlib import Path
p = Path('a/b/c/')
# p.mkdir() # No such file or directory: 'a/b/c'
p.mkdir(parents=True)
print(p.exists())
# p.mkdir(parents=True) # [Errno 17] File exists: 'a/b/c'
p.mkdir(parents=True, exist_ok=True)
p.rmdir()
p = Path('a/b')
p /= 'readme.txt' # 或 p.with_name('readme.txt').touch()
print(p.exists())
p.touch()
print(p.exists())
# p.parent.rmdir() # Directory not empty: 'a/b'
# 遍历目录,判断是否为文件类型,或者目录,如果是目录并判断是否为空
from pathlib import Path
'''
a
├── 1.py
├── b
│ └── readme.txt
└── c
'''
p = Path('a')
for i in p.iterdir():
if i.is_file():
print(f'{i} is file')
elif i.is_dir():
flag = False
for _ in i.iterdir():
flag = True
break
print(i, 'not empty' if flag else 'empty')
else:
print('other file')
# 在当前目录下创建目录d/e/1.txt
from pathlib import Path
p = Path('d/e/1.txt')
if not p.parent.exists():
p.parent.mkdir(parents=True)
p.touch(exist_ok=True)
通配符和匹配
- glob(pattern):通配给定的模式
- rglob(pattern):通配给定的模式,递归目录
- glob和rglob都返回一个生成器
?
一个字符,*
任意个字符,[a-z]
a-z中一个字符- match(pattern):匹配模式,成功则返回True
from pathlib import Path
'''
a
├── b
│ ├── b.py
│ └── b.zip
├── c
│ └── test.py
└── test1.py
'''
p = Path('a')
print(list(p.glob('test*')))
print(list(p.glob('**/*.py'))) # 递归所有目录,等同rglob
print(list(p.glob('**/*')))
g = p.rglob('*.py') # 生成器,递归
print(next(g))
print(list(p.rglob('*.???')))
print(list(p.rglob('[a-z]*.??')))
--------------mac上执行结果------------------
[PosixPath('a/test1.py')]
[PosixPath('a/test1.py'), PosixPath('a/c/test.py'), PosixPath('a/b/b.py')]
[PosixPath('a/test1.py'), PosixPath('a/c'), PosixPath('a/b'), PosixPath('a/c/test.py'), PosixPath('a/b/b.zip'), PosixPath('a/b/b.py')]
a/test1.py
[PosixPath('a/b/b.zip')]
[PosixPath('a/test1.py'), PosixPath('a/c/test.py'), PosixPath('a/b/b.py')]
文件信息
- stat() 相当于stat命令
- lstat() 同stat(),但如果是符号链接,则显示符号链接本身的文件信息
文件操作
- Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
- Path.read_bytes()
- Path.read_text(encoding=None, errors=None)
- Path.write_bytes(data)
- Path.write_text(data, encoding=None, errors=None)