一、open()
file = open(file_name [, mode='r' [ , buffering=-1 [ , encoding = None ]]])
file:表示要创建的文件对象。
file_name:要创建或打开文件的文件名称,该名称要用引号(单引号或双引号都可以)括起来。
如果要打开的文件和当前执行的代码文件位于同一目录,则直接写文件名即可;
否则,此参数需要指定打开文件所在的完整路径。
mode:可选参数,用于指定文件的打开模式。如果不写,则默认以只读(r)模式打开文件,文件不存在会报错
buffering:可选参数,用于指定对文件做读写操作时,是否使用缓冲区(本节后续会详细介绍)
buffing = 0(或者 False),则不使用缓冲区
buffing 参数值为大于 1 的整数,该整数用于指定缓冲区的大小(单位是字节);
buffing 参数值为负数,使用默认的缓冲区大小
encoding:手动设定打开文件时所使用的编码格式,不同平台的 encoding 的默认参数值也不同
以 Windows 为例,其默认为 cp936(实际上就是 GBK 编码)
可以在使用 open() 函数时,手动指定打开文件的编码格式,例如:
file = open("a.txt",encoding="utf-8")
注意,手动修改encoding 参数的值,仅限于文件以文本(r等)的形式打开,
也就是说,以二进制格式(rb等)打开时,不能对 encoding 参数的值做任何修改,
否则程序会抛出 ValueError 异常,如下所示:
ValueError: binary mode doesn't take an encoding argument'
同时以为rb等打开的文件也不能访问encoding属性 否则报错
AttributeError: '_io.BufferedReader' object has no attribute 'encoding'
>>> f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','r')
>>> print(f.encoding)
cp936
>>> f=open(r'H:\CSprogramsP\LeetCode\回文链表.cpp')
>>> print(f.encoding)
cp936
>>> f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','rb',encoding='uft-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: binary mode doesn''t take an encoding argument’
>>> f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','rb')
>>> print(f.encoding)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: '_io.BufferedReader' object has no attribute 'encoding'
注意encoding是说调用open打开文件时采用的编码方式 而不是说这个文件原本的编码方式
通常情况下、建议大家在使用 open() 函数时打开缓冲区,即不需要修改 buffing 参数的值。
内存的 I/O 速度仍远远高于外设(例如键盘、鼠标、硬盘等)的 I/O 速度,如果不使用缓冲区,
则程序在执行 I/O 操作时,内存和外设就必须进行同步读写操作,
也就是说,内存必须等待外设输入(输出)一个字节之后,才能再次输出(输入)一个字节。
这意味着,内存中的程序大部分时间都处于等待状态。
而如果使用缓冲区,则程序在执行输出操作时,会先将所有数据都输出到缓冲区中,
然后继续执行其它操作,缓冲区中的数据会有外设自行读取处理;
同样,当程序执行输入操作时,会先等外设将数据读入缓冲区中,无需同外设做同步读写操作。
Windows下关于python文件打开时SyntaxError OSError文件路径异常
SyntaxError: (unicode error) 'unicodeescape' codec cannot decode bytes
in position 2-3: truncated \UXXXXXXXX escape
OSError: [Errno 22] Invalid argument: 'C:\\Users\\cy\\Desktop\x07.txt'
文件路径
要么统一使用双反斜杠,(可+r也可不+r,推荐不+r)
>>> f=open(r'C:\\Users\\cy\\Desktop\\a.txt','r')
>>>> f=open('C:\\Users\\cy\\Desktop\\a.txt','r') 推荐
要么统一使用单但斜杠+r,(必须+r)
>>> f=open('C:\Users\cy\Desktop\a.txt','r')
File "<stdin>", line 1
SyntaxError: (unicode error) 'unicodeescape' codec cannot decode bytes
in position 2-3: truncated \UXXXXXXXX escape
>>> f=open(r'C:\Users\cy\Desktop\a.txt','r') 推荐
要么使用单正斜杠
>>> f=open('C:/Users/cy/Desktop/a.txt','r') 不推荐
前两者更推荐 第三种不推荐 同时路径不区分大小写
注意以上讨论都是在Windows操作系统下
不同操作系统下文件路径详解传送门(https://blog.csdn.net/Wjf7496/article/details/109695716)
成功打开文件之后,可以调用文件对象本身拥有的属性获取当前文件的部分信息,其常见的属性为:
file.name:返回文件的名称;
file.mode:返回打开文件时,采用的文件打开模式;
file.encoding:返回打开文件时使用的编码格式;
file.closed:判断文件是否己经关闭。
# 以默认方式r打开文件
f = open('my_file.txt')
# 输出文件是否已经关闭
print(f.closed)
# 输出访问模式
print(f.mode)
#输出编码格式
print(f.encoding) #访问不能是rb等二进制模式方式
# 输出文件名
print(f.name)
查看文件原本的编码方式(不是查看打开时的encoding) 安装第三方库chardet
pip install chardet
执行
import chardet
f = open('a.doc','rb')
data = f.read()
print (chardet.detect(data))
结果
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
前面是编码格式 后面是相似度
或者 return chardet.detect(data).get("encoding") 直接获取文件编码格式
结果
'GB2312'
如果文件打开模式用r报错 因为detect只能接受bytes 或者 bytearray 型参数
r打开文件按字符读得到的是data是字符串str不接受 因此需要二进制打开
TypeError: Expected object of type bytes or bytearray, got: <class 'str'>
二、文件读
read([size]) 函数:逐个字节或者字符读取文件中的内容;
文件以文本模式打开逐个字符读取;反之以二进制模式打开逐个字节读取
size指定一次最多可读取的字符(字节)个数,如果省略,则默认一次性读取所有内容
即便设置的 size 大于文件中存储的字符(字节)数也不会报错,它只会读取文件中所有的数据
readline([size]) 函数:逐行读取文件中的内容;
包含最后的换行符“\n”也要读
size限制每行最多可以读取的字符(字节)数
my_file.txt 文件为例,该文件中有如下 2 行数据:
Python教程
http://c.biancheng.net/python/
f = open("my_file.txt")
读取一行数据
byt = f.readline()
print(byt)
程序执行结果为:
Python教程
————————————————————————
readline() 函数在读取文件中一行的内容时,会读取最后的换行符“\n”,
再加上 print() 函数输出内容时默认会换行,所以输出结果中会看到多出了一个空行。
readlines() 函数:一次性读取文件中多行内容。
读取文件中的所有行,和调用不指定 size 参数的 read() 函数类似,
只不过该函数返回是一个字符串列表,其中每个元素为文件中的一行内容。
和 readline() 函数一样,readlines() 函数在读取每一行时,会连同行尾的换行符一块读取
f = open("my_file.txt",'rb')
byt = f.readlines()
print(byt)
运行结果为:
[b'Python\xbd\xcc\xb3\xcc\r\n', b'http://c.biancheng.net/python/']
read(),readline(),readlines()都要求 open() 函数
必须以可读默认(包括 r、r+、rb、rb+)打开文件。
注意这里暂时没考虑w+ a+等 虽然用w+,a+不会报错 但是没意义
因为前者打开时会清空原有文件
而a+文件指针又是在文件末尾
这两者执行读都只能读出空字符/字节串
my_file.txt 的文本文件,其内容为:
Python教程
http://c.biancheng.net/python/
#以二进制形式打开指定文件,该文件编码格式为 utf-8
f = open("my_file.txt",'rb+')
byt = f.read()
print(byt)
print("\n转换后:")
print(byt.decode('utf-8'))
#关闭文件
f.close()
程序执行结果为:
b'Python\xe6\x95\x99\xe7\xa8\x8b\r\nhttp://c.biancheng.net/python/'
转换后:
Python教程
http://c.biancheng.net/python/
输出的数据为 bytes 字节串。我们可以调用 decode() 方法,将其转换成我们认识的字符串。
io.UnsupportedOperation读写权限异常
使用 read() 函数成功读取文件内容,除了严格遵守 read() 的语法外,
其还要求 open() 函数必须以可读默认(包括 r、r+、rb、rb+)打开文件。
举个例子,将上面程序中 open()的打开模式改为 w,程序会抛出io.UnsupportedOperation异常,
提示文件没有读取权限:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\file.py", line 3, in <module>
byt = f.read()
io.UnsupportedOperation: not readable
UnicodeDecodeError编码异常
UnicodeDecodeError异常原因在于
目标文件使用的编码格式和 open() 函数打开该文件时指定的encoding使用的编码格式不匹配
如目标文件的编码格式为 GBK (cp936)编码,而我们在使用 open() 函数并以文本模式r打开该文件时
手动指定 encoding 参数为 UTF-8,这时去read()就会报错
>>> f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','r')
>>> print(f.encoding) 默认打开编码cp936
cp936
>>> f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','r',encoding='utf-8')
>>> print(f.encoding) 指定打开编码cp936
utf-8
>>> data = f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "D:\360Downloads\python\lib\codecs.py", line 322, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbf
in position 833: invalid start byte
这里报错是因为目标文件编码是GB2312 而我们指定了打开编码utf-8
可以通过前面讲的chardet查看目标文件编码
import chardet
f = open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','rb')
data = f.read()
print (chardet.detect(data))
输出
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
要解决这个问题
要么将 open() 函数中的 encoding 参数值修改为和目标文件相同的编码格式
f=open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','r',encoding='GB2312')
data=f.read()
要么重新生成目标文件(即将该文件的编码格式改为和 open() 函数中的 encoding 参数相同)
除此之外,还有一种方法(推荐):
先使用二进制模式rb读取文件
然后调用 bytes 的 decode() 方法,使用目标文件的编码格式,将读取到的字节串转换成认识的字符串。
如下
import chardet
f = open('H:\\CSprogramsP\\LeetCode\\回文链表.cpp','rb')
data = f.read()
print(data)
print(data.decode('GB2312'))
或者print(data.decode(chardet.detect(data).get("encoding")))
第二中写法更普适
文件常见异常
异常类型 | 异常信息 | 异常含义 |
---|---|---|
ValueError | binary mode doesn’t take an encoding argument’ | 二进制模式打开时指定encoding参数 |
AttributeError | ‘_io.BufferedReader’ object has no attribute encoding’ | 二进制模式打开后访问encoding参数 |
TypeError | Expected object of type bytes or bytearray, got: <class ‘str’> | decode()参数不是 bytes or bytearray |
io.UnsupportedOperation | ----- | 读写权限错误 |
UnicodeDecodeError | ------ | 目标文件编码与打开文件encoding参数指定编码不同 |
SyntaxError: | (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape | 文件路径格式错误 |
OSError: | [Errno 22] Invalid argument: ‘C:\Users\cy\Desktop\x07.txt’ | 文件路径格式错误 |
三、文件写
注意各种打开模式写会不会清空或者是追加写
需保证使用 open() 函数是以 r+、w、w+、a 或 a+ 的模式打开文件
否则执行 write() 函数会抛出 io.UnsupportedOperation 错误。
file.write(string)
在使用 write() 向文件中写入数据
file.writelines()
实现将字符串列表写入文件中。
向文件中写入多行数据时,不会自动给各行添加换行符。
f = open('a.txt', 'r')
n = open('b.txt','w+')
n.writelines(f.readlines())
n.close()
f.close()
执行此代码,在 a.txt 文件同级目录下会生成一个 b.txt 文件,
且该文件中包含的数据和 a.txt 完全一样。之所以 b.txt 文件中会逐行显示数据
是因为 readlines() 函数在读取各行数据时,读入了行尾的换行符
在写入文件完成后,一定要调用 close() 函数将打开的文件关闭,否则写入的内容不会保存到文件中。
通过设置 open() 函数的 buffering 参数可以关闭缓冲区,这样数据就可以直接写入文件中了?
对于以二进制格式打开的文件,可以不使用缓冲区,写入的数据会直接进入磁盘文件;
但对于以文本格式打开的文件,必须使用缓冲区,否则 Python 解释器会 ValueError 错误。例如:
f = open("a.txt", 'w',buffering = 0)
f.write("写入一行新数据")
运行结果为:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\demo.py", line 1, in <module>
f = open("a.txt", 'w',buffering = 0)
ValueError: can't have unbuffered text I/O
四、文件关闭与删除
open() 函数打开的文件在操作完成之后,一定要调用 close() 函数将其关闭吗?
答案是肯定的。不及时关闭的话程序的运行可能出现问题
import os
f = open("my_file.txt",'w')
#...
os.remove("my_file.txt")
引入入 os 模块,调用了该模块中的 remove() 函数删除指定的文件。
但是,如果运行此程序,Python 解释器会报如下错误:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\demo.py", line 4, in <module>
os.remove("my_file.txt")
PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。: 'my_file.txt'
因此在remove()之前必须执行close()
import os
f = open("my_file.txt",'w')
#...
f.close()
os.remove("my_file.txt")
正常删除文件my_file.txt不存在了
不调用 close() 函数关闭已打开的文件,不会影响读取文件的操作,但会导致 write() 或者 writeline() 函数向文件中写数据时,写入失败。例如:
f = open("my_file.txt", 'w')
f.write("http://c.biancheng.net/shell/")
程序执行后,虽然 Python 解释器不报错,但打开 my_file.txt 文件会发现,根本没有写入成功。
这是因为,在向以文本格式(而不是二进制格式)打开的文件中写入数据时,Python 出于效率的考虑,
会先将数据临时存储到缓冲区中,只有使用 close() 函数关闭文件时,才会将缓冲区中的数据真正写入文件中。
因此,在上面程序的最后添加如下代码:
f.close()
再次运行程序,就会看到 "http://c.biancheng.net/shell/" 成功写入到了 a.txt 文件。
当然在某些实际场景中,我们可能需要在将数据成功写入到文件中,但并不想关闭文件
这也是可以实现的,调用 flush() 函数即可,例如:
f = open("my_file.txt", 'w')
f.write("http://c.biancheng.net/shell/")
f.flush()
打开 my_file.txt 文件,会发现已经向文件中成功写入了字符串“http://c.biancheng.net/shell/”。
with-as语句
以上我们一直强调打开的文件最后一定要关闭,否则会程序的运行造成意想不到的隐患。
但是,即便使用 close() 做好了关闭文件的操作,
如果在打开文件或文件操作过程中抛出了异常,还是无法及时关闭文件。
为了更好地避免此类问题,不同的编程语言都引入了不同的机制。
Python 中,对应的解决方式是使用 with as 语句操作上下文管理器(context manager),
它能够帮助我们自动分配并且释放资源。
语法格式:
with 表达式 [as target]:
代码块
target 参数用于指定一个变量,该语句会将 expression 指定的结果保存到该变量中。
with as 语句中的代码块如果不想执行任何语句,可以直接使用 pass 语句代替。
假设有一个 a.txt 文件,其存储内容如下:
C语言中文网
http://c.biancheng.net
在和 a.txt 同级目录下,创建一个 .py 文件,并编写如下代码:
with open('a.txt', 'a') as f:
f.write("\nPython教程")
运行结果为:
C语言中文网
http://c.biancheng.net
Python教程
可以看到,通过使用 with as 语句,即便最终没有关闭文件,修改文件内容的操作也能成功。
五、文件指针
file.tell() 函数用于判断文件指针当前所处的位置,返回值是int型
f = open("a.txt",'r')
print(f.tell())
type(f.tell())
print(f.read(3))
print(f.tell())
运行结果
0
<class 'int'>
htt
3
file.seek(offset[, whence]) 函数用于移动文件指针到文件的指定位置。
file:表示文件对象;
whence:指定文件指针要放置的位置,该参数的参数值有 3 个选择:
0 代表文件头(默认值)、
1 代表当前位置、
2 代表文件尾。
offset:表示相对于 whence 位置文件指针的偏移量,正数表示向后偏移,负数表示向前偏移。例如,
当whence==0&&offset==3(即 seek(3,0))表示文件指针移动至距离文件开头处 3 个字符的位置;
当whence==1&&offset==5(即 seek(5,1))表示文件指针向后移动,移动至距离当前位置 5 个字符处。
offset非0时,Python 要求文件必须要以二进制格式打开否则会抛出 io.UnsupportedOperation 错误。