读写文件
读
通过内置函数open()
来打开一个文件,并返回文件对象,若无法被打开,则会抛出OSError。
文件内容
你好
张三
法外狂徒
林黛玉风雪山神庙
read
如果文件很小,read()
一次性读取最方便
file = open("222.txt", mode="r", encoding="utf-8")
print(type(file)) #打印文件对象的类型
print(file.read()) #一次性读取文件所有的内容,这就注定它没办法读取大文件
file.close() #打开文件之后,一定要将文件关闭,否则会一直占用着内存
结果
<class '_io.TextIOWrapper'>
你好
张三
法外狂徒
林黛玉风雪山神庙
read之后剩下了什么?
with open("222.txt", mode="r", encoding="utf-8") as f:
print("第一次read")
print(f.read())
print("第二次read")
print(f.read())
第一次read
你好
张三
法外狂徒
林黛玉风雪山神庙
唐僧大战霸天虎
第二次read
Process finished with exit code 0
我们发现一个问题,第一次read完了之后,第二次啥也没读到,就可以想象成锅里有N个饼(从文件中获取内容到缓存中),一锅端到了盆里(从缓存中取出来打印出来),锅里就空了
with
好了,已经对读文件有一定认识了,但是我们要中断一下,上面我们读取文件的方式有弊端,就是我们必须在最后关闭文件,那下面两种情况,可能造成我们没有正常关闭文件,造成系统资源的浪费
1、调皮,就是忘了写了
2、打开了文件,但是在close之前程序报错了,执行不到close了
第一种还好,人眼校验,第二点就难受了呀,有一种解决办法就是通过try…finally的方式解决,但是还是很繁琐的,为了解决这个问题,引入了with,下面这种方式会自动帮我们调用close
with open("222.txt", mode="r", encoding="utf-8") as f:
print(type(f))
print(f.read())
<class '_io.TextIOWrapper'>
你好
张三
法外狂徒
林黛玉风雪山神庙
是不是和第一个例子是一样的,以后我们就这么写了,不要按照之前的方式写了。
read(size)
调用read()
会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)
方法,每次最多读取size个字节的内容
如果不能确定文件大小,反复调用read(size)
比较保险
至于怎么使用,说实在的我在实际使用中也没用过这个,因为平时读取比较多的是配置文件,但是抄来了一份答案
def readlines(f, separator):
'''
读取大文件方法
:param f: 文件句柄
:param separator: 每一行的分隔符
:return:
'''
buf = ''
while True:
while separator in buf:
position = buf.index(separator) # 分隔符的位置
yield buf[:position] # 切片, 从开始位置到分隔符位置
buf = buf[position + len(separator):] # 再切片,将yield的数据切掉,保留剩下的数据
chunk = f.read(4096) # 一次读取4096的数据到buf中
if not chunk: # 如果没有读到数据
yield buf # 返回buf中的数据
break # 结束
buf += chunk # 如果read有数据 ,将read到的数据加入到buf中
with open('text.txt',encoding='utf-8') as f:
for line in readlines(f,'|||'):
# 为什么readlines函数能够使用for循环遍历呢, 因为这个函数里面有yield关键字呀, 有它就是一个生成器函数 ......
print(line)
readline
调用readline()
可以每次读取一行内容
with open("222.txt", mode="r", encoding="utf-8") as f:
print(type(f))
print(f.readline())
<class '_io.TextIOWrapper'>
你好
我们发现,这个readline只能读取一行啊,那我们怎么读取多行呢。
在读取文件所有内容之前,我们来升级一下文件,中间加入一个空行
你好
张三
法外狂徒
林黛玉风雪山神庙
唐僧大战霸天虎
with open("222.txt", mode="r", encoding="utf-8") as f:
done = 0
while not done: # 0为False,not False = True
line = f.readline()
if line != "": # 如果读取的内容不为空
print(line.strip()) # 打印这一行的内容,strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)
else:
done = 1 # 如果readline读取的内容为空了,就结束循环
你好
张三
法外狂徒
林黛玉风雪山神庙
唐僧大战霸天虎
这里可能很多人有疑问,是不是遇到一个空行,也会被认为是文件的结束呢?事实上,文件的空白行并不会返回一个空行。因为在每一行的末尾还有一个或者多个分隔符,因此“空白行”至少会有一个换行符或者系统使用的其他符号。所以,即使文件中真的包含一个“空白行”,读入的行也不是空的,这就意味着在真实遍历读取到文件结束之前,程序实际上是不会停止的。
readlines
调用readlines()
一次读取所有内容并按行返回list
如果是配置文件,调用readlines()
最方便
with open("222.txt", mode="r", encoding="utf-8") as f:
for i in f.readlines():
print(i.strip())
print(type(f.readlines()))
你好
张三
法外狂徒
林黛玉风雪山神庙
唐僧大战霸天虎
<class 'list'>
with open("222.txt", mode="r", encoding="utf-8") as f:
print(f.readlines())
['你好\n', '张三\n', '法外狂徒\n', '林黛玉风雪山神庙\n', '\n', '唐僧大战霸天虎']
我们发现,换行是\n,空白行也是\n,读取出来的空白行并不是""
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
rb和encoding
python3里面所有的字符都是utf-8的形式,如果在打开一个文件的时候我不知道这个编码怎么办?
那我们就不指定encoding的编码,那么Python3默认就是utf8但是这样也不行,同时我们要改变读的模式,才可以,r就是文本模式,可以直接读取字符串的,如果用户不知道文件的格式的话可以不指定编码格式,同时直接使用rb的模式,就是硬盘怎么存储的你就怎么存到内存,直接以二进制的形式,就可以了
错误示范
with open("one.jpg", mode="r") as f:
print(f.readlines())
报错
Traceback (most recent call last):
File "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py", line 2, in <module>
print(f.readlines())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/codecs.py", line 322, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
正确打开方式
with open("one.jpg", mode="rb") as f:
print(f.readlines())
一个图片通过二进制文件打开也是很大的,就只展示出一部分算了
d7\xc4\xa5\x92\xbe]4H\x06\x9d8\xa6Aa\x8d\x15b%\xa3tD\x84\x8fUL\xa1F\xb7\x95\xc7\xf6G\xf4\xf6\xa0\x96T\x0b\xe7\xd5Y\xdbN:Vm\xac\xbd2\xe4`\xa6\x9eS\xea\x93J>\xb0\xaa\xd5\x04\x1d1)\xfa\xf8\xd3\x9b\xff\x00\xaf\xed|\x12+\xfe\x93\xf9\x0c}\xbd*\x82M/W\xe1\xe5\xf6\xf4\xae\x97r\xd6\xe2e\x9a\xb9*\x8a\x1aZ\x91\x03\x95vT}\x12i\x88\xd8\x8e}#\x8f\xf6\xfe\xdcm\xd2{5\x0c~ ~\xdf\xe5\xd2\xcf\x0c:\x96\x93$\xff\x00\x83\xfc\xfd\x0f;
。。。。。。。
该何如知道文件的编码格式呢?
chardet!
安装命令
pip3 install chardet
使用
import chardet
result = chardet.detect(open("222.txt", mode="rb").read())
print(result)
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
遇到不规范文件怎么办
遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError
,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()
函数还接收一个errors
参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略
反面教材,使用gbk编码读取utf-8编码文件
with open("222.txt", mode="r", encoding="gbk") as f:
print(f.read())
报错
Traceback (most recent call last):
File "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py", line 2, in <module>
print(f.read())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 18: illegal multibyte sequence
如果我们忽略报错呢
with open("222.txt", mode="r", encoding="gbk", errors='ignore') as f:
print(f.read())
结果
浣犲ソ
寮犱笁
娉曞栫媯寰
鏋楅粵鐜夐庨洩灞辩炲簷
鍞愬儳澶ф垬闇稿ぉ铏
发现虽然编码不对,但至少不报错了,没有中断程序
写
write
with open("222.txt", mode="r") as f:
print("文件被写之前")
print(f.read())
with open("222.txt", mode="w") as f:
f.write("为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢")
with open("222.txt", mode="r") as f:
print("文件被写之后")
print(f.read())
结果
文件被写之前
你好
张三
法外狂徒
林黛玉风雪山神庙
唐僧大战霸天虎
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
我们发现,文件被覆盖了呀,其实
w不是修改,是创建了一个新的文件名字,如果和原来的旧文件有名字一样,原来的文件就是清空,如果是文件名字不一样就是新建,所以我们要是小心使用:w
追加模式
啊,覆盖了我的文件,我不想啊,我只想在文件最后添加内容,那我们就需要使用mode=“a”
with open("222.txt", mode="r") as f:
print("文件被写之前")
print(f.read())
with open("222.txt", mode="a") as f:
f.write("这次是追加的内容了")
with open("222.txt", mode="r") as f:
print("文件被写之后")
print(f.read())
文件被写之前
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢这次是追加的内容了
我们发现直接加到了文件的最后,而且是同行展示,想要跨行展示,只需要将
```
f.write(“这次是追加的内容了”)
```
修改成
f.write("\n这次是追加的内容了")
writelines
a = ["\n", "重生之我是刘姥姥\n", "刘姥姥之宝玉爱上我"]
with open("222.txt", mode="r") as f:
print("文件被写之前")
print(f.read())
with open("222.txt", mode="a") as f:
f.writelines(a)
with open("222.txt", mode="r") as f:
print("文件被写之后")
print(f.read())
文件被写之前
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
重生之我是刘姥姥
刘姥姥之宝玉爱上我
文件路径
文件路径分为相对路径和绝对路径,相对路径
是指一个文件或文件夹所在路径与其它文件或文件夹的路径关系,而绝对路径
是指从盘符(即磁盘区)到当前位置的路径。
判断文件或文件夹的路径是否为绝对路径
import os
print(os.path.isabs("222.txt"))
print(os.path.isabs("/Users/zc/PycharmProjects/pythonProject1/test/222.txt"))
False
True
获取文件绝对路径
import os
print(os.path.abspath("222.txt"))
/Users/zc/PycharmProjects/pythonProject1/test/222.txt
获取当前路径
import os
path1 = os.getcwd()
path2 = os.path.dirname(__file__) #更常用
print(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test
/Users/zc/PycharmProjects/pythonProject1/test
判断路径是否存在
import os
print(os.path.exists("/Users/zc/PycharmProjects/pythonProject1/test"))
print(os.path.exists("/Users/zc/PycharmProjects/pythonProject1/tes2"))
True
False
返回所在路径
如果入参是路径,那就是返回上一层
import os
path1 = os.getcwd()
print(path1)
path2 = os.path.dirname(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test
/Users/zc/PycharmProjects/pythonProject1
如果入参是文件,那就返回文件的路径
import os
path1 = "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py"
path2 = os.path.dirname(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test
拼接路径
import os
path1 = os.path.dirname(__file__)
path2 = os.path.join(path1, "112.txt")
path3 = os.path.join(path1, "222.txt")
print(path2)
print(path3)
/Users/zc/PycharmProjects/pythonProject1/test/112.txt
/Users/zc/PycharmProjects/pythonProject1/test/222.txt