python学习笔记09:文件&目录操作
1. 打开文件的模式
普通
模式 | 名称 | 说明 |
---|---|---|
r | 以只读方式打开文件。 | 文件的指针将会放在文件的开头。这是默认模式。 |
w | 打开一个文件只用于写入。 | 如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。 | 如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
rb | r的二进制模式 | - |
wb | w的二进制模式 | - |
ab | a的二进制模式 | - |
r+ | r的读写模式 | 不会创建不存在的文件从顶部开始写会覆盖之前此位置的内容,如果先读后写,则会在文件最后追加内容。 |
w+ | w的读写模式 | 如果文件存在,则覆盖整个文件,如果不存在,则创建。要close 之后才算完成写入 |
a+ | a的读写模式 | 可读可写,从文件顶部读取内容,从文件底部添加内容,不存在则创建 |
rb+ | r的二进制+读写模式 | |
wb+ | w的二进制+读写模式 | |
ab+ | a的二进制+读写模式 |
模式 | 读 | 写 | 文件不存在时 | 是否覆盖 |
---|---|---|---|---|
r | Y | - | 报错 | - |
r+ | Y | Y | 报错 | Y |
w | - | Y | 创建 | Y |
w+ | Y | Y | 创建 | Y |
a | - | Y | 创建 | N, 追加写 |
a+ | Y | Y | 创建 | N, 追加写 |
2. 写文件
例1: 推荐做法: with … as: 不需要手工关闭文件;
>>> with open('tmp.txt', 'w') as fh: #使用with 打开,使用完fh后,不需要手工关闭之;
... fh.write('line0\n') # 需要手工加换行符
... fh.write('line1\n')
...
例2, 常规做法, 打开文件,写入文件,关闭文件;推荐使用with的方法,不需要手工关闭文件
>>> fh = open('tmp.txt', 'w')
>>> fh.write('line0\n') # 注意不会自动换行,需要手工加换行符;
>>> fh.write('line1\n')
>>> fh.close() # 使用完后需要关闭
3. 读文件
例1: 推荐做法, with … as: 不需要手工关闭文件
>>> with open('tmp.txt', 'r') as fh: #使用with 打开文件,不需要手工关闭文件
... for line in fh:
... print(line, end='') # 行尾不加换行, 或者print(line.strip('\n'))
...
输出:
line0
line1
如果待打开的文件中包含中文, 会导致报告UnicodeDecodeError: 'gbk' codec can't decode byte …, 这时, 可以给open加编码参数:
open('tmp.txt', 'r', encoding='UTF-8')
同时获得行号与行文本:
for lineno, line in enumerate(fh, 1)): # 行号以1开头,而不是以0开头
print(lineno)
例2: 常规做法:打开文件,读取文件,关闭文件;推荐with的做法
>>> fh = open('tmp.txt') #没有声明打开的模式,默认read模式,即第二个参数默认为r
>>> while True:
... line = fh.readline()#readline,读取一行内容
... if len(line) == 0:#长度等于0表示文件结尾,注意空行的长度为1,换行符占一个长度
... break
... print(line)
...
>>> fh.close()
输出:
line0
<空行> #因为print会默认在结尾加换行符
line1
<空行>
几个read函数
>>> fh.read() #读取整个文件,将之放到一个字符串变量中;
>>> fh.readline() #每次只读取一行,放到字符串中;
>>> fh.readlines() #读取整个文件,将之分析成一个列表;
例3:
>>> with open('tmp.txt', 'r') as fh:
... txt = fh.read() # 'line0\nline1\n',读取所有行,返回str;
例4:
>>> with open('tmp.txt', 'r') as fh:
... txt0 = fh.readline()# 'line0\n',每次读取一行;
... txt1 = fh.readline().strip() # 'line1',每次读取一行, str.strip()可以去行尾换行符和空格;
例5:
>>> with open('tmp.txt', 'r') as fh:
... list_txt = fh.readlines() # ['line0\n', 'line1\n'],读取所有行,放到list中;
4. 读取/写入压缩文件(.gz)
使用gzip模块读取gz文件:
读取
import gzip
with gzip.open('file.txt.gz', 'rb') as fid: # 读取模式为rb
for line in fid:
line = line.decode() # 读入的line是二进制格式(b''), 需要解码
line = line.strip('\n') # 去掉换行符
print(line)
写入:
import gzip
fh = gzip.open('file.txt.gz', 'wb')
fh.write('1234'.encode())
fh.close()
5. with…as语句
可以使用with语句代替try…finally语句,好处是语法比较简洁。另见第15.2节。
不管在处理文件过程中是否发生异常,都能保证with语句执行完毕后关闭打开的文件句柄。
>>> with open(file, 'r') as f:
... print(f.read())
等价于如下写法:
>>> try:
... f = open(file, 'r')
... print(f.read())
... finally:
... if f:
... f.close()
...
6. 文件测试/文件判断
1.判断文件/目录是否存在
>>> os.path.exists(test_file) # True | False
>>> os.path.exists(test_dir)# True | False
2.判断文件是目录或是普通文件
>>> os.path.isfile(test_target) # test_target是普通文件时,返回True, "是目录"或"目录不存在"时,返回False;
>>> os.path.isdir(test_target) # test_target是目录时,返回True, "是普通文件"或"文件不存在"时,返回False;
3.判断文件是否可读写
>>> os.access(test_file, os.F_OK) # 文件存在,返回True;
>>> os.access(test_file, os.R_OK) # 文件可读,返回True;
>>> os.access(test_file, os.W_OK) # 文件可写,返回True;
>>> os.access(test_file, os.X_OK) # 文件可执行,返回True;
7. 文件、目录、路径
获取程序所在目录:
#假设py文件 /test/path/a.py的内容为
import os, sys
print(f'os.getcwd() = {os.getcwd()}')
print(f'sys.path[0] = {sys.path[0]}')
#用户在/home/g444054/目录下执行
$ python /test/path/a.py
os.getcwd() = /home/g444054 # 返回的是执行命令的路径
sys.path[0] = /test/path # 返回的是脚本所在的路径
列出指定目录下的文件(指定目录为空时,默认为当前目录),返回列表,注意:返回的list元素不包含对应的path,只是文件名。要递归目录, 需要使用os.walk
>>> os.listdir(path)
['file0', 'file1', 'dir0']
创建目录, 删除目录, 删除文件, 复制文件
>>> os.mkdir(path)# 创建目录;
>>> os.rmdir(path)# 删除目录, 要递归删除使用shutil.rmtree;
>>> os.remove(file) # 删除文件;
>>> os.rename('test.txt', 'test.py') # 重命名
>>> shutil.copy('/home/g004440**/.cshrc', '/home/n004435**/') #复制文件
>>> shutil.copytree('/home/g004440**/dir0', '/home/g004440**/dir.bak') #复制目录和内部所有文件. 目标文件夹不能已经存在.
>>> shutil.rmtree(path) # 删除目录树, 必须指向文件夹, 不能是符号链接.
切换目录
>>> os.chdir(path)# 切换目录
文件大小(字节)
>>> os.path.getsize(filename) #返回文件大小,对目录无效
65525
拆分指定路径: os.path.split(path)(必须有一个参数),返回一个包含两个元素的元组:
[0]: 文件或目录所在的路径,也可以通过os.path.dirname(path)获取
[1]: 目录名或文件名
>>> os.path.split('/home/g004440**/script')
('/home/g004440**', 'script')
>>> os.path.split('/home/g004440**/script/') # 末尾包含'/',全部归到路径
('/home/g004440**/script', '') # 不管script是不是一个目录
>>>
>>> os.path.split('/home')
('/', 'home')
>>> os.path.split('/home/')
('/home', '')
拆分文件扩展名,返回一个元组
>>> os.path.splitext('/home/g004440**/.vim.rc')
('/home/g004440**/.vim', '.rc')
合并路径,如果Linux会用/合并,Win中是\
>>> os.path.join('/home', 'g00444054', 'script') # 合并路径,
'/home/g004440**/script'
>>> os.path.join('/home', 'g00444054', 'script', '')
'/home/g004440**/script/' # 末尾有空元素会多返回一个'/'
>>> os.path.join('', '/home', '', 'g00444054', 'script')
'/home/g004440**/script' # 前面或中间有空元素,不影响结果.
获取路径的绝对路径os.path.abspath(path)或os.path.realpath(path)
>>> os.path.abspath('.')
'/home/g004440**/script'
>>> os.path.abspath('..')
'/home/g004440**'
>>> os.path.abspath('../xxx') # xxx可以不存在。
'/home/g004440**/xxx'
>>> os.path.realpath('../xxx') # xxx可以不存在。
'/home/g004440**/xxx'
8. 遍历目录操作
目录结构test_walk
test_walk/
|-- dir0
|-- dir1
| |-- dir1.file0
| |-- dir1.file1
|-- dir2
| |-- dir2.dir0
| |-- dir2.dir1
|-- file0
`-- file1
代码
for _s_one_path, _list_dirs, _list_files in os.walk('test_walk'):
print(f'{_s_one_path}/') #test_walk目录中的所有层级目录
for _s_one_dir in _list_dirs: #当前_s_one_path目录中一个层级中的dir
print(f' {_s_one_dir}/')
for _s_one_file in _list_files: #当前_s_one_path目录中一个层级中的file
print(f' {_s_one_file}')
print('')
输出:
test_walk/
dir0/ # test_walk目录中的3个dir
dir1/
dir2/
file0 # test_walk目录中的2个file
file1
test_walk/dir0/ # test_walk/dir0目录中没有dir和file
test_walk/dir1 # test_walk/dir0目录中没有dir, 有2个file
dir1.file0
dir1.file1
test_walk/dir2 # test_walk/dir2目录中有2个dir, 没有file
dir2.dir0/
dir2.dir1/
test_walk/dir2/dir2.dir0 # test_walk/dir2/dir2.dir0目录中没有dir和file
test_walk/dir2/dir2.dir1 # test_walk/dir2/dir2.dir1目录中没有dir和file