open()函数详解
要点概论:
1. file 与 mode 参数
2. buffering,encoding,errors,newlines参数
3. stdin,stdout,stderr
1. file 与 mode 参数
file 可以是个字符串,指定读写文件的路径,可以指定相对路径或者绝对路径;
mode 是使用字符串来指定文件打开的模式。
先看一个基本款的文件打开模式表格:
字符串 | 说明 |
r | 读取模式(默认) |
w | 写入模式,会先清空文件内容 |
x | 只在文件不存在时才创建新文件并打开为写入模式,若文件已存在会引发 FileExistsError |
a | 追加模式,若文件已存在,写入的内容会附加到文件尾端 |
b | 二进制模式 |
t | 文本模式(默认) |
+ | 更新模式(读取与写入) |
open() 的 mode 默认是 r ,在只指定 r , w ,x ,a 的情况下就相当于以文本模式打开,也就等同于 rt , wt , xt , at ,
如果要以二进制模式打开,就要指定 rb , wb ,xb ,ab。
如果想以更新模式打开,对于文本模式,可以使用 r+ , w+ , a+ ,对于二进制模式,可以使用 r+b , w+b , a+b。
1)read(),write(),close()
首先来看一个通用的 upper 程序,可使用命令行参数指定源与目标,将源文本文件的内容全部转成大写后写入目标文件:
with open('小写字母表') as low,open('大写字母表','w') as upper: content = low.read() upper.write(content.upper())
read() 方法在未指定自变量的情况下会读取文件全部的内容。并返回 str 实例(二进制文件返回 bytes 实例),因此例子中可以使用 upper() 转换为大写。
write() 方法可将指定的数据写入文件,write() 接受 str 实例并返回写入的字符数(二进制文件接受 bytes 实例并返回写入的字节数)。
PS:read() 方法在指定整数自变量的情况下会读取指定的字符数(二进制为字节数)。
文件打开模式如果与后续进行的操作不符合,会引发 Unsupported Operation 异常,可以使用 readable() 方法测试是否可读取,writable() 方法测试是否可写入:
f = open('测试表') f.write('是否能写入') # io.UnsupportedOperation: not writable print(f.readable()) # True print(f.writable()) #False
2)readline(),readlines(),writelines()
文本模式和二进制模式通用。
对于文本模式来说,默认读取到 'n','\r 或 '\r\n' 时都可以被判定为一行,而readline() 或 readlines() 读到的每一行换行符都一律换为 '\n' 。
对于二进制模式来说,行的判断标准默认时遇到 'b\n' 这个 bytes 类型。
文本模式在写入的情况下,任何 '\n' 都会被置换为 os.linesep 的值(Window 就是 '\r\n')。
下面看一个'rb'模式打开文件,readlines() 的结果:
f = open('测试表','rb') print(f.readlines()) #[b'111\r\n', b'222\r\n', b'333\r\n', b'\r\n', b'\r\n', b'666'] f.close()
可以看到,readlines()返回一个 列表(list),列表里每个元素时文件中被判定为一行的内容。
下面是使用 readline() 搭配 while 循环的例子,因为 readline() 在都不到下一行时会返回空字符串,所以这样写:
with open('测试表') as f: while True: line = f.readline() if not line: break print(line,end='')
因为 open() 返回的文件对象都实现了 __iter__() 方法,可以返回一个迭代器,因此可以直接是哟个 for in 来进行迭代,每次迭代相当于执行 readline():
with open('测试表') as f: for line in f: print(line,end='')
PS:python的文件读取风格时:读取一个文件最好的方式就是不要去 read 。
3)tell(),seek(),flush()
在进行文件读取时, tell() 方法可以告知文件指针当前阿紫文件中的位置,单位时字节数,文件开头的位移量时 0 , seek() 方法可以指定跳到哪个位移量,如下例所示:
with open('test','rb') as f: print(f.tell()) # 0 print(f.read(1)) # b'1' print(f.read(1)) # b'2' print(f.tell()) # 2 print(f.seek(1)) # 1 print(f.read(1)) # b'2'
使用 seek() 来实现文件的随机存取,例如将第三个字节改为 b'0' :
with open('test','r+b') as f: f.seek(2) print(f.write(b'0')) # 1 f.flush() f.seek(0) print(f.read()) # b'12045'
主要不要写成 'w+b', 这样会清空文件内容,也不要写成 'a+b' ,这样会将写入的数据放到文件尾端。
由于文件对象默认会缓冲处理,不一定会马上看到文件中写入了数据,这时可以执行 flush() 方法,将缓冲内容清空并写入文件。
4)readto()
在二进制模式中, read() 方法返回的 bytes 是不可变动的,如果想将读取到的字节数据收集到一个列表(list)中,基本上可以使用 list() ,例如:
info = None with open('test2','rb') as f: info = f.read() print(list(info)) #[49, 50, 51, 52, 53] #test2 中文件内容为 12345
除此之外,在二进制模式中的文件对象拥有一个 readinto() 方法接受 bytearray 实例,可以直接将读取到的数据传入,这样就可以不用中介的变量,例如:
import os.path b_arr = bytearray(os.path.getsize('test2')) with open('test2','rb') as f: f.readinto(b_arr) print(b_arr[0]) # 49 print(b_arr[1]) # 50 print(b_arr) # bytearray(b'12345')
PS:两者通过索引获取的每个元素都是 int 类型,可以通过 bytes([value]) 将整数值转为 bytes ,例如 bytes([49]) 结果会是 b'1'。
2. buffering,encoding,errors,newlines 参数
open() 函数一共有8个参数,除了常用的 file 与 mode 参数,这一部分介绍 buffering,encoding,errors 与 newlines 参数(closed,opener参数待补充)。
1)buffering
这个参数用来设置缓冲策略,默认的缓冲策略会试着自行决定缓冲区大小(通常为 4096 或 8192 字节),或者对互动文本文件( isatty() 为 True 时,例如 Windows的命令提示符)采取缓冲(line buffering)。
buffering 设置为 0 表示关闭缓冲,设置为大于 0 的整数值表示指定缓冲区的字节大小。
在前面的一个例子中如果采用 with open('test','r+b',buffering=0) as f ,在 f.write 更新文件之后不必使用 f.flush() ,马上就可以看到文件内容的变化:
with open('test','r+b',buffering=0) as f: f.seek(2) print(f.write(b'0')) # 1 # f.flush() f.seek(0) print(f.read()) # b'12045'
2)encoding 与 errors
encoding 是指定文件读取编码的参数。
在 Windows 简体中文版中默认编码是 'cp936' 或 'gbk' (在 python 执行环境中,将 MS936 ,CP936,gbk 视为同一套编码)。
errors 参数可指定发生编码错误时该如何处理。
在不设置的情况下,发生编码错误时会引发 ValueError 的子类异常。
例如 test3 文件中有 ‘方法已弃用’ 这几个汉字,并采用 utf-8 编码,如下方法读取,将会引发 UnicodeDecodeError 错误:
with open('test3') as f: print(f.read()) #UnicodeDecodeError: 'gbk' codec can't decode byte 0xa8 in position 14: incomplete multibyte sequence
如果设置 errors 为 ‘ignore’ ,那么会忽略错误,继续读取的操作,如下例所示:
with open('test3',errors='ignore') as f: print(f.read()) # 鏂规硶宸插純鐢
errors 的其他可设置选项请参考 open() 函数的说明
3)newlines
对于文本模式来说,默认读取到 '\n' ,'\r' 或 '\r\n 都可以被判定为一行,
而 readline() 或 readlines() 读到的每一行一律保留来源的换行字符,如果设置为其他 '\n' ,'\r' ,'\r\n' ,那么读取后的换行字符就会时指定的字符。
文本模式在写入的默认情况下,任何 '\n' 都会被置换为 os.linesep 的值。
如果 newlines 设为 ‘’ 就保留原有的换行字符,如果指定为其他值,就以指定的字符进行置换。
3. stdin ,stdout ,stderr
目前获取用户的输入都是使用 input() 函数,若想显示指定的值,则使用 print() 函数,它们各自会使用预先连接的设备进行输入或输出。
预先连接的输入,输出设备被称为标准输入(Standard input)与标准输出(Standard output),以个人计算机而言,通常对应到终端的输入与输出。
在 sys 模块中有个 stdin 代表标准输入,stdout 代表标准输出,它们的操作就像 open() 函数打开的文本模式的文件对象。
下面的例子模仿了 input() 函数的实现:
import sys def console_input(prompt): sys.stdout.write(prompt) # 使用标准输出 sys.stdout.flush() # 缓存清出数据到标准输出 return sys.stdin.readline() #使用标准输入读取一行 name = console_input('请输入名称:') print('hello',name)
在上面这个例子中, sys.stdout 的 write() 方法 “写出” 信息,为了能马上看到指定的信息显示,必须使用 flush() 方法清出数据(即将缓冲区中的数据清出到标准输出),
接着可以使用 sys.stdin.readline() 读入一行输入的信息。
对于标准输入或输出,若想要以二进制模式读取或写入,可以使用 sys.stdin.buffer 或 sys.stdout.buffer ,它们的操作或行为就像以 open() 函数打开的二进制模式的文件对象。
实际上可以改变标准输入或输出的来源,例如,将一个以 open() 函数打开的文件对象赋值给 sys.stdin 就可以利用 input() 来读取:
import sys sys.stdin = open('stdin_demo.py',encoding='utf-8') print(input()) # import sys print(input()) # def console_input(prompt): print(input()) # sys.stdout.write(prompt) # 使用标准输出 print(input()) # sys.stdout.flush() # 缓存清出数据到标准输出 print(input()) # return sys.stdin.readline() #使用标准输入读取一行 print(input()) # name = console_input('请输入名称:') print(input()) # print('hello',name) print(input()) # EOFError: EOF when reading a line
类似地,将一个打开的文件对象赋值给 sys.stdout 就可以利用 print() 把数据写到文件中。
不过内建的 print() 函数本身就有一个 file 参数可以满足这样的需求:
with open('data.txt','w') as f: print('hello',file=f)
上面的程序执行过后,当前工作目录下就会创建一个 data.txt 文件(已有则覆盖),内容就是执行 print() 函数时输出的 'hello' 信息。