“Life is short, You need Python” – Bruce Eckel
Environment
本文记录一些 Python 打开关闭文件的基本方式和读取写入的方法,主要以文本文件为例。而在实际操作中,更多的会使用第三方库(如Pandas 中 Series 和 DataFrame 对象的方法;open-cv 或 PIL 的函数)对结构化数据或图片数据进行读取或保存。
要对文件内容进行操作,首先需要使用 open
函数打开文件,得到文件指针;要对写入的内容进行保存,需要关闭文件,调用文件指针的 close
方法。
在 .py 文件同目录下放一个 zen.txt 文件,内容如下
open(file='文件路径', mode='打开模式', encoding='操作文件的字符编码')
返回文件对象 f
。当文件与当前的 Python 文件在同一目录,则可以直接对 file 传入文件名。f.close()
调用方法关闭文件例
# 非绝对路径时,会在 Python 同目录下寻找该文件
f = open('zen.txt', 'r', encoding='utf-8')
f.read()
# 'Beautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.'
f.close()
如上所述,在对文件操作完成后,需要调用 f.close()
关闭文件,这样才会将修改写入硬盘。注意,由于 Python 有垃圾回收机制,在文件指针对象的引用数为零时将其销毁,所以不会造成内存泄露。
Alternatively,更常用 with
加缩进的结构,可以不用调用 close
,在 with
结构运行结束之后文件将自动关闭
with open('文件路径', '打开模式', encoding='操作文件的字符编码') as f:
对文件进行的操作
例
with open('zen.txt', 'r', encoding='utf-8') as f:
f.read()
# 'Beautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.'
open
函数中的 mode
参数传入的值将确定读写模式和文件类型
读写模式 | 打开模式 | 注 |
---|---|---|
‘r’ | 只读 | 默认。若文件不存在则报错 ;文件指针在开头 |
‘r+’ | 读写 | 若文件不存在则报错;文件指针在开头,写入多少覆盖多少原有的内容 |
‘w’ | 只写 | 创建新文件,若存在同名文件则以空白文件覆盖之(危) |
‘w+’ | 读写 | (同上) |
‘x’ | 只写 | 创建新文件;若文件已存在则报错 |
‘a’ | 只写 | 若文件不存在则创建空白文件;若文件已存在则打开文件,指针在末尾,实现追加写入内容 |
‘a+’ | 读写 | (同上) |
注:默认为文本格式(‘t’),如 mode='w+'
就是 mode='wt+'
;若需要以二进制格式打开,则传入参数加上 ‘b’,如 mode='rb+'
,这时不传入 encoding。
例 将爬取的图片写入到硬盘
# 爬取的图片使用变量 img 引用,那么
with open('Lenna.jpg', 'wb') as f:
f.write(img)
个人遇到过使用 Pandas 打开 csv 文件编码错误的情况,编码问题有时候是比较麻烦的问题,解决办法就是查查资料,然后试试传入各种 encoding 参数 ╯▽╰ 详细的编码查看官网的Python标准编码。
内置函数 open
函数中的 encoding
参数传入的值将文件的字符编码,当处理文本文件时,建议应该显式地传入 encoding 参数,清楚地指示文件的编码。
Python 3.x 默认使用 ‘UTF-8’ 编码,使用 locale
库的 getpreferredencoding
函数能够获得当前默认编码
import locale
locale.getpreferredencoding() # 'UTF-8'
关于文件对象的方法,传入的参数基本都为“仅位置参数”(参考Python基础(五)函数的2.3)。
查了一些资料,都显示 open 函数返回的是一个“文件对象”,在 Python 中使用 type 函数查看这个返回值,为如下结果
# 以文本模式打开时
f = open('zen.txt', 'r', encoding='utf-8')
print(type(f)) # _io.TextIOWrapper
f.close()
# 以二进制模式打开时
f = open('zen.txt', 'rb')
print(type(f)) # _io.BufferedReader
f.close()
个人认为可以理解为一个文件指针对象,因为读写操作都是从这个位置开始的,并且有如下方法
f.tell()
返回文件指针当前的位置with open('zen.txt', 'r', encoding='utf-8') as f:
print('Position of pointer in the beginning:', f.tell())
content = f.read(6)
print('Position of pointer now:', f.tell())
# Position of pointer in the beginning: 0
# Position of pointer now: 6
f.seek(cookie, whence=0, /)
从 whence
位置(对 whence
传入0 为开始(默认),1 位当前位置,2 位末尾)开始,偏移 cookie
个字符位置。注意,只有当以二进制模式打开文件时,才可以对 whence
传入 1 或 2。返回指针的新位置。with open('zen.txt', 'rb') as f:
print('Position of pointer in the beginning:', f.tell())
f.seek(11, 1)
print('Position of pointer now:', f.tell())
f.truncate(pos=None, /)
从文件的第一个字符开始,到之后的 pos 位置为止,截取这些内容保留,之后的内容全部删除。默认传入 None,全部截断,情况内容。返回保留的内容的长度,即传入的 poswith open('zen.txt', 'r+') as f:
new_size = f.truncate(16)
print(f.read())
# Beautiful is bet
print(new_size) # 16
注意,读取方法中参数 (size / hint)传入时直接传入数字,而不允许以关键字形式传入。
f.read(size=-1, /)
若传入负值(默认),则返回从文件指针当前位置到文件结束的所有内容,文件指针移动到文件结束位置;若传入正整数,则文件指针向后移动 size 个字符的位置,返回 size 个字符;若传入零,则文件指针位置不变,不读取字符with open('zen.txt', 'r', encoding='utf-8') as f:
print(f.read())
# Beautiful is better than ugly.
# Explicit is better than implicit.
# Simple is better than complex.
# Complex is better than complicated.
# Flat is better than nested.
# Sparse is better than dense.
f.readline(size=-1, /)
若传入负值(默认),则返回当前文件指针位置开始,到下一个换行符为止(包括换行符)的内容;若传入正整数,则文件指针向后移动,直到遇见了换行符或移动了 size 个字符为止,返回文件指针经过的内容;若传入零,则文件指针位置不变,不读取字符with open('zen.txt', 'r', encoding='utf-8') as f:
print(f.readline(2))
print(f.readline(), end='')
# Be
# autiful is better than ugly.
f.readlines(hint=-1, /)
若传入负值或零,则返回一个列表,其中每个元素为文件中的每行,包含换行符;若传入正整数,文件指针当前位置开始,向后移动 hint 个字符的位置,再从这个位置(如果这个位置非换行符)开始,移动到下一个换行符为止。这些文件指针经过的内容,以每个换行符分开,作为列表的元素返回。with open('zen.txt', 'r', encoding='utf-8') as f:
print(f.readlines(31))
print(f.readlines())
# ['Beautiful is better than ugly.\n', 'Explicit is better than implicit.\n']
# ['Simple is better than complex.\n', 'Complex is better than complicated.\n', 'Flat is better than nested.\n', 'Sparse is better than dense.']
with open('zen.txt', 'r', encoding='utf-8') as f:
for row in f:
print(row, end='')
# Beautiful is better than ugly.
# Explicit is better than implicit.
# Simple is better than complex.
# Complex is better than complicated.
# Flat is better than nested.
# Sparse is better than dense.
以这个列表为例
lines = [
'Beautiful is better than ugly.\n',
'Explicit is better than implicit.\n',
'Simple is better than complex.\n',
'Complex is better than complicated.\n',
'Flat is better than nested.\n',
'Sparse is better than dense.'
]
text = ''
for line in lines:
text += line
f.write(text, /)
将 text 写入文件,返回写入的字符数with open('zen.txt', 'w', encoding='utf-8') as f:
f.write(text)
# 188
f.writelines(lines)
返回 None。传入一个可迭代序列对象,注意如需换行,要加上换行符with open('zen.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)