总结一下python3文件操作
文件操作分为读,写,修改。
读文件
示例1:
现有以下文件:
‘兼职1’ (utf-8编码格式)
王心 深圳 159 46 13813234424 马纤羽 深圳 173 50 13744234523 乔亦菲 广州 172 52 15823423525 罗梦竹 北京 175 49 18623423421 刘诺涵 北京 170 48 18623423765 岳妮妮 深圳 177 54 18835324553 贺婉萱 深圳 174 52 18933434452 叶梓萱 上海 171 49 18042432324 杜姗姗 北京 167 49 13324523342 black girl 河北 167 50 13542342233
f = open(file='兼职1', mode='r', encoding='utf-8') data = f.read() print(data) f.close()
语法操作解释:
file='兼职1' 表示文件路径
mode='r' 表示只读
encoding='utf-8' 表示将硬盘上的 0101010 按照utf-8的规则去'断句',再将'断句'后的每一段0101010转换成unicode的01010101, unicode对照表中有0101010和字符的对应关系。
f.read() 表示读取所有内容,内容是已经转换完毕的字符串。
f.close() 表示关闭文件,文件操作完之后一定要进行此操作,要不然文件内容就一直留在内存中,浪费内存而且占用着文件。
##此处encoding必须和文件在保存时设置的编码一致,不然'断句'会不准确从而造成乱码。
在windows中,如果encoding省略不指定,则会按系统默认编码去打开文件,也就是说用gbk去打开utf-8的文件,则会乱码。
示例2:
f = open(file='兼职1',mode='rb') data = f.read() f.close()
#打印出来的内容是bytes
上述操作语法解释:
file = '兼职1' 表示文件路径
mode='rb' 表示只读(可以修改为其他)
f.read() 表示读取所有内容,内容是硬盘上原来以某种编码保存的010101010,即:某种编码格式的字节类型 (所以不用encoding)
f.close() 表示关闭文件
示例1和示例2的区别:
示例2打开文件时并未指定encoding,是因为直接以rb模式打开了文件,rb是指二进制模式,数据读到内存里直接是bytes格式,如果想要内容,还需要手动decode,因此在文件打开阶段,不需要指定编码。
如果我要处理的文件不知道是什么编码的时候,可以用chardet函数来判断一下。
import chardet f = open('log',mode='rb') data = f.read() f.close() result = chardet.detect(f.read()) print(result)
输出:
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
注意:
- 文件操作时,以 “r”或“rb” 模式打开,则只能读,无法写入;
- 硬盘上保存的文件都是某种编码的0101010,打开时需要注意:
- rb,直接读取文件保存时原生的0101010,在Python中用字节类型表示
- r和encoding,读取硬盘的0101010,并按照encoding指定的编码格式进行断句,再将“断句”后的每一段0101010转换成unicode的 010101010101,在Python中用字符串类型表示
循环文件
f = open("兼职1",'r',encoding="utf-8") for line in f: print(line) f.close()
写文件
f = open(file='兼职1',mode='w',encoding='utf-8') f.write('北大本科美国留学一次50,微信号:xxxxx') f.close()
上述语法解释:
file='兼职1' 表示文件路径
mode='r' 表示只写
encoding='utf-8' 将要写入的unicode字符串编码成utf-8格式
f.write() 表示写入内容,写入的内容是unicode字符串类型,内部会根据encoding转换为指定编码的01010101,即:字节类型
f.close()
二进制写
f = open(file='兼职1',mode='wb') f.write('北大本科美国留学一次50,微信号:xxxxx'.encode('utf-8')) f.close()
上述语法解释:
file='兼职1' 表示文件路径
mode='wb' 表示只以2进制模式写
f.write(...) 表示写入内容,写入的内容必须字节类型,即:是某种编码格式的0101010
f.close()
注意:
文件操作时,以 “w”或“wb” 模式打开,则只能写,并且在打开的同时会先将内容清空。
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
- wb,写入时需要直接传入以某种编码的0100101,即:字节类型
- w 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
追加
把内容追加到文件尾部
f = open("兼职1",'a',encoding="gbk") f.write(" 杜姗姗 北京 167 49 13324523342") f.close()
运行结果
姓名 地区 身高 体重 电话 况咏蜜 北京 171 48 13651054608 ...... 岳妮妮 深圳 177 54 18835324553 贺婉萱 深圳 174 52 18933434452 叶梓萱 上海 171 49 18042432324 杜姗姗 北京 167 49 13324523342 #这行是添加的
注意:
文件操作时,以 “a”或“ab” 模式打开,则只能追加,即:在原来内容的尾部追加内容
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
- ab,写入时需要直接传入以某种编码的0100101,即:字节类型
- a 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
读写模式
打开模式只有只读、只写、只追加,难道没有可以读写的操作吗?当然有
读写模式(先读后写)
f = open("兼职白领学生空姐模特护士联系方式.txt",'r+',encoding="gbk") data = f.read() #可以读内容 print(data) f.write(" black girl 河北 167 50 13542342233") #可以写 f.close()
上面写入的内容是写到了文件的最后面,后面还有方式可以写到任意位置。
写读模式
f = open("兼职白领学生空姐模特护士联系方式.txt",'w+',encoding="gbk") data = f.read() print(data) f.write(" newline 1哈哈") f.write(" newline 2哈哈") f.write(" newline 3哈哈") f.write(" newline 4哈哈") print("content",f.read()) f.close()
输出
#注意这是个空行, 是上面print(data)的结果,代表 根本 没读到内容
content #从这开始,读到的是刚写入的内容
newline 1哈哈
newline 2哈哈
newline 3哈哈
newline 4哈哈
此时查看文件 内容 发现,里面只有4条newline..内容,之前的旧内容全没了,事实代表,w+会先把文件清空,再写新内容,相比w模式,只是支持了一个读功能,且还只能读已经写入的新内容。(没什么用)
文件操作的其他功能
def fileno(self, *args, **kwargs): # real signature unknown 返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到 def flush(self, *args, **kwargs): # real signature unknown 把文件从内存buffer里强制刷新到硬盘 def readable(self, *args, **kwargs): # real signature unknown 判断是否可读 def readline(self, *args, **kwargs): # real signature unknown 只读一行,遇到 or 为止 def seek(self, *args, **kwargs): # real signature unknown 把操作文件的光标移到指定位置 *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。 如“天下无双” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“下”和“无”两个字中间。 但如果是utf8,seek(4)会导致,拿到了下这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节 def seekable(self, *args, **kwargs): # real signature unknown 判断文件是否可进行seek操作 def tell(self, *args, **kwargs): # real signature unknown 返回当前文件操作光标位置 ,以字节为单位 def truncate(self, *args, **kwargs): # real signature unknown 按指定长度截断文件 *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。 def writable(self, *args, **kwargs): # real signature unknown
修改文件
尝试直接以r+模式打开文件,默认会把新增的内容追加到文件最后面。但我想要的是修改中间的内容 ,怎么办? 为什么会把内容添加到尾部呢?(最新测试r+会从头覆盖,测试代码如下)
f1 = open("test.txt",'w',encoding="utf-8") f1.write("[irving]") f1.close() f = open("test.txt",'r+',encoding="utf-8") f.write("test") f.close()
文件追加操作之所以内容会追加到最后面,是因为,文件一打开,要写的时候,光标会默认移到文件尾部,再开始写。 想修改中间部分,seek(中间位置)再写就可以了。
f = open("兼职1",'r+',encoding="utf-8") f.seek(6) f.write("[天下无双]") f.close()
输出
王心[天下无双]9 46 13813234424 马纤羽 深圳 173 50 13744234523 乔亦菲 广州 172 52 15823423525 罗梦竹 北京 175 49 18623423421 刘诺涵 北京 170 48 18623423765 岳妮妮 深圳 177 54 18835324553 贺婉萱 深圳 174 52 18933434452 叶梓萱 上海 171 49 18042432324 杜姗姗 北京 167 49 13324523342 black girl 河北 167 50 13542342233
结果:从第三个字开始,而且把后面的内容覆盖了。
这是硬盘的存储原理导致的,当你把文件存到硬盘上,就在硬盘上划了一块空间,存数据,等你下次打开这个文件 ,seek到一个位置,每改一个字,就是把原来的覆盖掉,如果要插入,是不可能的,因为后面的数据在硬盘上不会整体向后移。所以就出现 当前这个情况 ,你想插入,却变成了会把旧内容覆盖掉。
这里,有两种办法解决上述问题。
- 占用硬盘修改
- 占用内存修改
占用硬盘修改:
可以在打开旧文件 的同时,生成一个新文件,边从旧的里面一行行的读,边往新的一行行写,遇到需要修改就改了再写到新文件 ,这样,在内存里一直只存一行内容。就不占内存了。 但这样也有一个缺点,就是虽然不占内存 ,但是占硬盘,每次修改,都要生成一份新文件,虽然改完后,可以把旧的覆盖掉,但在改的过程中,还是有2份数据 的。(os.remove, os.rename 结合这两个方法进行覆盖。)
占用内存修改:
把内容全部读到内存里,数据在内存里可以随便增删改查,修改之后,把内容再全部写回硬盘,把原来的数据全部覆盖掉。vim word等各种文本编辑器都是这么干的。(可能需要结合seek和truncate来操作)
占硬盘方式的文件修改代码示例
f_name = "兼职1.txt" f_new_name = "%s.new" % f_name old_str = "乔亦菲" new_str = "[乔亦菲 Yifei Qiao]" f = open(f_name,'r',encoding="utf-8") f_new = open(f_new_name,'w',encoding="utf-8") for line in f: if old_str in line: new_line = line.replace(old_str,new_str) else: new_line = line f_new.write(new_line) f.close() f_new.close()
上面的代码,会生成一个修改后的新文件 ,原文件不动,若想覆盖原文件
import os f_name = "兼职白领学生空姐模特护士联系方式utf8.txt" f_new_name = "%s.new" % f_name old_str = "乔亦菲" new_str = "[乔亦菲 Yifei Qiao]" f = open(f_name,'r',encoding="utf-8") f_new = open(f_new_name,'w',encoding="utf-8") for line in f: if old_str in line: new_line = line.replace(old_str,new_str) else: new_line = line f_new.write(new_line) f.close() f_new.close() os.rename(f_new_name,f_name) #把新文件名字改成原文件 的名字,就把之前的覆盖掉了,windows使用os.replace # 帮助文档说明replace会覆盖原文件
#windows rename不会覆盖,如果用rename前必须先move掉原文件