Python 文件操作
1. open() 方法
2. os 模块
3. shutil 模块
1. open() 方法
1.1 文件的打开与关闭
open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。
open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。
注意:使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。
f = open(file, "r")
f.close()
若使用 with open(),则文件对象在操作完后会自动 close。
with open(file, "r") as f: # 操作文件 # 无需 f.close()
open()方法完整的参数有:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
- file: 必需,文件路径(相对或者绝对路径)
- mode: 可选,文件打开模式,默认为"r"
- buffering: 设置写数据时的缓冲区大小
- encoding: 一般使用 utf8,默认为操作系统默认编码
- errors: 报错级别
- newline: 区分换行符
- closefd: 传入的file参数类型
常见的访问模式
写模式 注意事项:
- 带“w”的写模式为“清空写”,即每次写之前会先清空文件原有内容;
- 其余像带“+”的写模式则不会清空原有内容,仅会覆盖写范围的内容。
访问模式 | 说明 |
---|---|
r | (read)以只读方式打开文件(这是默认模式),文件的指针将会放在文件的开头。若文件不存在则报错。 |
w |
(write)打开一个文件只用于写入。如果该文件已存在则将其覆盖;如果该文件不存在,创建新文件。 |
a |
(add)打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。 也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
rb | (b:binary)以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头(这是默认模式)。 |
wb |
以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖;若该文件不存在,创建新文件。 |
ab |
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。 也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
r+ |
打开一个文件用于读写,文件指针将会放在文件的开头。若文件不存在则报错。 若使用seek()定位到了文件中的某个位置,则从该位置开始写;若使用read()改变了游标的位置,则从文件末尾开始写。 |
w+ |
打开一个文件用于读写。若该文件已存在则将其覆盖;若文件不存在则创建新文件。 |
a+ |
打开一个文件用于读写。若该文件已存在,文件指针将会放在文件的结尾,即文件打开时会是追加模式。 若该文件不存在,创建新文件用于读写。 |
rb+ |
以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
wb+ |
以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖;若该文件不存在,创建新文件。 |
ab+ |
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾;若该文件不存在,创建新文件用于读写。 |
1.2 文件的读写
常见的写数据函数
- write(str_obj):将一个字符串写入文件中。
- writelines(list_obj):将一个列表写入文件中,列表中的每个元素是文件中的一行,注意需要自己为每个元素末尾添加换行符。
1 f = open("test.txt", "w") # 以写模式在当前目录打开test.txt 2 f.wirte("Hello world, I am here.") # 写入内容 3 f.close() # 关闭文件对象
注意:一般的文件流操作都包含缓冲机制,write 方法并不直接将数据写入文件,而是先写入内存中特定的缓冲区。
- flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区。
- 正常情况下缓冲区满时,操作系统会自动将缓冲数据写入到文件中。
- close() 方法原理是内部先调用 flush() 方法来刷新缓冲区,再执行关闭操作,这样即使缓冲区数据未满也能保证数据的完整性。
- 如果进程意外退出或正常退出时而未执行文件的 close() 方法,缓冲区中的内容将会丢失。
1 # 文件对象未关闭的前提下 2 >>> f = open("e:\\file1.txt", "w") 3 >>> f.write("1") # 此时数据“1”未真正写入文件中 4 1 5 >>> f.flush() # 此时数据“1”真正写入了文件中 6 >>> f.write("2") # 此时数据“2”未真正写入文件中 7 1 8 >>> f.seek(0,0) # 此时数据“2”真正写入了文件中,文件内容为“12” 9 0
常见的读数据函数
注意:如果多次使用了读函数,那么后面函数读取的数据是从上次函数读完后的位置开始的。
- read([num]):num表示要从文件中读取的数据的长度(单位是字节)。如果没有传入num,那么就表示读取文件中所有的数据。
- readlines([hint]]:hint参数是字节的总大小,会读取到该文件内对应字节数的当前行。如果没有传入参数则按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。
-
假设第一行有5个字符,若hint=5,则只读取第一行内容。(hint<=5 时都只会读取第一行的内容)
-
假设第二行有5个字符,若hint=6,则会读取前两行内容。(5<hint<=11 时会读取到前两行的内容)
-
- readline():只读取一行数据。
1 >>> with open("test.txt", "w") as f: 2 ... f.write("Hello world.\ni am here.\nwhat about you?") 3 ... 4 39 5 >>> f = open("test.txt", "r") 6 >>> f.read(5) # 读取5个字节的数据 7 'Hello' 8 >>> f.read() # 读取余下全部数据 9 ' world.\ni am here.\nwhat about you?' 10 >>> f.close() 11 >>> with open("test.txt", "r") as f: 12 ... f.readline() # 读取一行数据 13 ... 14 'Hello world.\n' 15 >>> with open("test.txt", "r") as f: 16 ... f.readlines() 17 ... 18 ['Hello world.\n', 'i am here.\n', 'what about you?']
二进制读写
1 # 若使用二进制模式读写文件,则不能指定encoding参数,否则报错 2 >>> with open("e:\\file1.txt", "wb", encoding="utf-8") as f: 3 ... f.write("嘻哈".encode("utf-8")) 4 ... 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 ValueError: binary mode doesn't take an encoding argument 8 9 # 正确写二进制内容 10 >>> with open("e:\\file1.txt", "wb") as f: 11 ... f.write("嘻哈".encode("utf-8")) 12 ... 13 6 14 15 # 读二进制内容 16 >>> with open("e:\\file1.txt","rb") as f: 17 ... print(f.read()) # 不指定编码 18 ... 19 b'\xe5\x98\xbb\xe5\x93\x88' 20 >>> with open("e:\\file1.txt", "rb") as f: 21 ... print(f.read().decode("utf-8")) # 指定存储时选择的编码 22 ... 23 嘻哈 24 >>> with open("e:\\file1.txt", "rb") as f: 25 ... print(f.read().decode("gbk")) # 指定其他编码 26 ... 27 鍢诲搱
处理大文件的方式
1 old_file = "old_file.txt" 2 new_file = "new_file.txt" 3 4 old_f = open(old_file, "r") 5 new_f = open(new_file, "w") 6 7 while 1: 8 # 每次从旧文件读取1024字节大小的数据写入新文件 9 content = old_f.read(1024) 10 if len(content) == 0: 11 break 12 new_f.write(content) 13 14 old_f.close() 15 new_f.close()
应用:文件备份
1 file_name = input("请输入要拷贝的文件名:") 2 3 # 取文件的后缀名 4 file_suffix_index = file_name.rfind(".") 5 if file_suffix_index > 0: 6 file_suffix = file_name[file_suffix_index:] 7 8 # 命名新文件名 9 new_file_name = file_name[:file_suffix_index] + "_backup" + file_suffix 10 11 # 读取旧文件数据到新文件中 12 old_file = open(file_name, "r") 13 new_file = open(new_file_name, "w") 14 15 while 1: 16 content = old_file.read(1024) 17 print(content) 18 if len(content) == 0: 19 break 20 new_file.write(content) 21 22 old_file.close() 23 new_file.close()
tell():定位当前位置
在读写文件的过程中,如果想知道文件游标的当前位置,可以使用 tell() 来获取。
1 # 打开一个已经存在的文件 2 f = open("file.txt", "r") 3 str = f.read(3) 4 5 # 查找当前位置 6 position = f.tell() 7 print("当前文件位置 : ", position) # 3 8 9 str = f.read(3) 10 11 # 查找当前位置 12 position = f.tell() 13 print("当前文件位置 : ", position) # 6 14 15 f.close()
seek(offset, from):设置游标位置
在读写文件的过程中,如果需要从另外一个位置进行操作,可以使用 seek()。
seek(offset, from):
- offset:偏移量
- 正数:往右偏移
- 负数 :往左偏移
- from:方向
- 0:表示从文件开头开始
- 1:表示从当前位置开始(仅支持二进制模式的操作)
- 2:表示从文件末尾开始(仅支持二进制模式的操作)
1 # 打开一个已经存在的文件 2 f = open("test.txt", "r") 3 str = f.read(30) 4 print("读取的数据是 : ", str) 5 6 # 查找当前位置 7 position = f.tell() 8 print("当前文件位置 : ", position) 9 10 # 重新设置位置:从文件开头偏移5个字节 11 f.seek(5, 0) 12 13 # 查找当前位置 14 position = f.tell() 15 print("当前文件位置 : ", position) 16 17 str = f.read() 18 print("读取的数据是 : ", str) # 文件最后3个字节数据 19 20 f.close()
2. os 模块
2.1 文件/目录相关操作
os 模块提供了非常丰富的方法用来处理文件和目录。
目录相关操作
1 # 获取当前工作目录 2 os.getcwd() 3 # 改变工作目录为 4 os.chdir(path) 5 6 # 遍历当前目录下的所有文件与子目录 7 os.listdir(path) 8 9 # 创建单级目录 10 os.mkdir(path) 11 # 删除单级目录 12 os.rmdir(path) # 仅可删除空目录 13 14 # 创建多级目录 15 os.makedirs("dir_path") 16 # 删除多级目录 17 os.removedirs("test1\\test2\test3") # 如果test3为空,则删除test3;如果test2为空,则删除test2;如果test2不为空,则停止删除 18 19 # 目录拼接(支持多级) 20 os.path.join(parent_dir, child_dir)
除了 listdir(path),第二种遍历目录和文件的方式是 os.walk(path):
1 # 深度优先遍历 2 >>> for root, dirs, files in os.walk("e:\\python_pra"): 3 ... print(root, dirs, files) 4 ... 5 e:\python_pra ['test', 'test2', '__pycache__'] ['main.py', 'new_file.txt', 'old_file.txt', 'old_file_backup.txt', 'test.py', 'test.txt', 'test2.py'] 6 e:\python_pra\test ['test111'] ['1.py'] 7 e:\python_pra\test\test111 [] [] 8 e:\python_pra\test2 [] ['2.py'] 9 e:\python_pra\__pycache__ [] ['test.cpython-38.pyc']
文件相关操作
1 # 判断path是否为文件(文件不存在也为False) 2 os.path.isfile(path) 3 # 判断path是否为目录(目录不存在也为False) 4 os.path.isdir(path) 5 # 判断目录/文件是否存在 6 os.path.exists(path) 7 8 # 返回文件的绝对路径(该文件不需要一定存在,返回结果只是拼接当前目录与文件名) 9 >>> os.path.abspath("file.txt") 10 'E:\\file.txt' 11 12 # 判断是否绝对路径 13 >>> os.path.isabs("e:\\file.txt") # 该文件不需要一定存在 14 True 15 >>> os.path.isabs("file.txt") 16 False 17 18 # 文件重命名 19 os.rename(old_file_name, new_file_name) 20 # 文件删除 21 os.remove(new_file_name) 22 23 # 分割文件名与扩展名,返回列表 24 file_name = os.path.splitext(file_name)[0] 25 ext_name = os.path.splitext(file_name)[1] 26 27 # 分割目录部分与文件部分 28 # 方法1:返回列表 29 dir_path = os.path.split(dir_and_file)[0] 30 file_name = os.path.split(dir_and_file)[1] 31 # 方法2:分别返回 32 >>> os.path.dirname("e:\\test\\file.txt") # 返回路径部分 33 'e:\\test' 34 >>> os.path.basename("e:\\test\\file.txt") # 返回文件名部分 35 'file.txt' 36 >>> os.path.dirname("file.txt") 37 '' 38 39 # 查看文件相关信息(如文件字节大小、创建时间等) 40 >>> os.stat("e:\\file1.txt") 41 os.stat_result(st_mode=33206, st_ino=7599824371219212, st_dev=877170276, st_nlink=1, st_uid=0, st_gid=0, st_size=2, st_atime=1596949917, st_mtime=1596949915, st_ctime=1592639679) 42 >>> os.stat("e:\\file1.txt").st_size 43 2 44 # atime:access time 访问时间 45 # mtime:modify time 修改时间 46 # ctime:create time 创建时间 47 48 # 获取文件大小(字节) 49 >>> os.path.getsize("file.txt") 50 43 51 52 # 获取文件创建时间 53 >>> os.path.getctime("file.txt") 54 1596959307.2522843 55 56 # 查看文件权限 57 >>> os.access("e:\\file1.txt", os.W_OK) # 写权限 58 True 59 >>> os.access("e:\\file1.txt", os.R_OK) # 读权限 60 True 61 >>> os.access("e:\\file1.txt", os.X_OK) # 执行权限 62 True
示例题
1. 设计实现遍历目录与子目录,抓取 .py 文件,并返回绝对目录:
1 import os 2 import glob 3 4 my_file_list = [] 5 6 7 # 方式一 8 def catch_my_file1(dir, suffix): 9 if os.path.exists(dir): 10 my_file_list.extend(glob.glob("{}\\*{}".format(dir, suffix))) 11 for paths in os.listdir(dir): 12 if os.path.isdir(paths): 13 catch_my_file1(os.path.join(dir, paths), suffix) 14 else: 15 print("需要遍历的目录不存在") 16 17 18 # 方式二 19 def catch_my_file2(dir, suffix): 20 if os.path.exists(dir): 21 for root, child_dirs, files in os.walk(dir): # 当前目录、所有子目录、所有子文件 22 for file in files: 23 name, suf = os.path.splitext(file) # 分割文件名与后缀名 24 if suf == suffix: 25 my_file_list.append(os.path.join(root, file)) 26 else: 27 print("需要遍历的目录不存在") 28 29 30 # catch_my_file1("e:\\python_pra", ".py") 31 catch_my_file2("e:\\python_pra", ".py") 32 print(my_file_list)
2.2 系统相关操作
查看系统默认的分隔符等信息:
1 >>> os.sep # 查看系统默认的目录分隔符 2 '\\' 3 >>> os.linesep # 查看系统默认的换行符 4 '\r\n' 5 >>> os.pathsep # 查看系统默认的环境变量的分隔符 6 ';' 7 >>> os.environ # 查看系统的环境变量,如windows中的Path配置
os.popen(command [, mode='r' [, bufsize]]):执行操作系统命令(如shell、DOS命令),返回一个存储了执行结果的文件对象
1 >>> import os 2 >>> with os.popen("ver") as f: 3 ... print(f.read()) 4 ... 5 6 Microsoft Windows [版本 10.0.18362.959] 7 8 >>> with os.popen("ver > file.txt") as f: # 若将执行结果保存到文件中,则文件对象中已无可读取内容 9 ... print(f.read()) 10 ... 11 12 >>>
3. shutil 模块
1 import shutil 2 3 # 复制文件:把src文件拷贝一份为dst文件,前提是目标地址是具备可写权限。抛出的异常信息为IOException。如果当前的dst已存在的话就会被覆盖掉 4 shutil.copyfile(src, dst) 5 6 shutil.move(src, dst) # 移动文件或重命名 7 8 # 在copy上的基础上再复制文件最后访问时间与修改时间也复制过来了,类似于cp –p 9 shutil.copy2(src, dst) 10 11 # 复制文件夹:把olddir拷贝一份到newdir。如果第3个参数是True,则复制目录时将保持文件夹下的符号连接;如果是False,则将在复制的目录下生成物理副本来替代符号连 12 shutil.copytree(olddir, newdir, True/Flase) 13 14 # 递归删除一个目录以及目录内的所有内容 15 shutil.rmtree(src)