文件目录操作
文件操作
文件准备:
致橡树.txt
编码格式为: gb2312
致橡树
我如果爱你---
绝不像攀援的凌霄花,
借你的高枝炫耀自己;
我如果爱你---
绝不学痴情的鸟儿,
为绿荫重复单调的歌曲;
也不止像泉源,
常年送来清凉的慰藉;
也不止像险峰,
增加你的高度,衬托你的威仪。
甚至日光,
甚至春雨。
不,这些都还不够!
我必须是你近旁的一株木棉,
作为树的形象和你站在一起。
根,紧握在地下;
叶,相触在云里。
每一阵风过,
我们都互相致意,
但没有人,
听懂我们的言语。
你有你的铜枝铁干,
像刀,像剑,也像戟;
我有我红硕的花朵,
像沉重的叹息,
又像英勇的火炬。
我们分担寒潮、风雷、霹雳;
我们共享雾霭、流岚、虹霓。
仿佛永远分离,
却又终身相依。
这才是伟大的爱情,
坚贞就在这里:
爱---
不仅爱你伟岸的身躯,
也爱你坚持的位置,
足下的土地。
问题:文件在硬盘上是如何存储?
答:以某种编码格式的 “010101010101001” 【二进制的字节流】保存在硬盘上。这些编码例如utf-8
、gb2312
、gbk
等等。
读取文件
fr = open(file="致橡树.txt", mode='r', encoding='utf-8')
data = fr.read()
fr.close()
print(data)
*运行后报错 *
Traceback (most recent call last):
File "/Users/hhjie/Documents/git-hehongjie/Python-Luffycity/文件操作与函数/测试/读文件-01.py", line 4, in <module>
data = fr.read()
File "/usr/local/anaconda3/lib/python3.6/codecs.py", line 321, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd4 in position 0: invalid continuation byte
报错原因,在于字符编码的不对,因为文件本身是gb2312
编码存在硬盘文件中的,是二进制的gb2312格式的编码文件。
而代码中打开文件到内存中的时候用的编码 encoding='utf-8'
,因此就会出现该错误。
如何正确运行?文件编码为哪种,那么就转换到内存中就用哪种编码。
文件的编码为gb2312
,那么读取到内存中代码用的编码也就应该为gb2312
即 encoding='gb2312'
fr = open(file="致橡树.txt", mode='r', encoding='gb2312')
data = fr.read()
fr.close()
print(data)
运行后,文件可以正确读出来并显示了。
file="致橡树.txt" 表示文件路径
mode='r' 表示只读(可以修改为其他)
encoding='utf-8' 表示将硬盘上的 0101010 按照utf-8的规则去“断句”,再将“断句”后的每一段0101010转换成unicode的 01010101,unicode对照表中有01010101和字符的对应关系。
f.read() 表示读取所有内容,内容是已经转换完毕的字符串。
f.close() 表示关闭文件
二进制形式读取文件
如下代码为错误的。
fr = open(file="致橡树.txt", mode='rb', encoding='gb2312')
data = fr.read()
fr.close()
print(data)
如果用了rb
模式打开,则不能再加encoding='gb2312
否则会报错
Traceback (most recent call last):
File "/Users/hhjie/Documents/git-hehongjie/Python-Luffycity/文件操作与函数/测试/读文件-二进制打开.py", line 3, in <module>
fr = open(file="致橡树.txt", mode='rb',encoding='gb2312')
ValueError: binary mode doesn't take an encoding argument
如下代码为正确的。但是无法显示文件中的内容,而是二进制的内容。
fr = open(file="致橡树.txt", mode='rb')
data = fr.read()
fr.close()
print(data)
结果为:二进制的结果。
b'\xd6\xc2\xcf\xf0\xca\xf7\n\xce\xd2\xc8\xe7\xb9\xfb\xb0\xae\xc4\xe3??\n\xa1\xa1\xa1\xa1\xbe\xf8\xb2\xbb\xcf\xf1\xc5\xca\xd4\xae\xa2\xd9\xb5\xc4\xc1\xe8\xcf\xf6\xbb\xa8\xa3\xac\n\xa1\xa1\xa1\xa1\xbd\xe8\xc4\xe3\xb5\xc4\xb8\xdf\xd6\xa6\xec\xc5\xd2\xab\xd7\xd4\xbc\xba\xa3\xbb\n\xa1\xa1\xa1\xa1\xce\xd2\xc8\xe7\xb9\xfb\xb0\xae\xc4\xe3??\n\xa1\xa1\xa1\xa1\xbe\xf8\xb2\xbb\xd1\xa7\xb3\xd5\xc7\xe9\xb5\xc4\xc4\xf1\xb6\xf9\xa3\xac\n\xa1\xa1\xa1\xa1\xce\xaa\xc2\xcc\xd2\xf1\xd6\xd8\xb8\xb4\xb5\xa5\xb5\xf7\xb5\xc4\xb8\xe8\xc7\xfa\xa3\xbb\n\xa1\xa1\xa1\xa1\xd2\xb2\xb2\xbb\xd6\xb9\xcf\xf......'
怎样能正确显示文件中的内容呢?
fr = open(file="致橡树.txt", mode='rb')
data = fr.read()
fr.close()
print(data.decode('gb2312')) # 这里将文件中保存的`gb2312`编码的内容,以`gb2312`编码格式解码到内存中的`unicode`编码,因此就能正确显示文件中的内容了。
file="致橡树.txt" 表示文件路径
mode='rb' 表示只读(可以修改为其他)
f.read() 表示读取所有内容,内容是硬盘上原来以某种编码保存的 010101010,即:某种编码格式的字节类型
f.close() 表示关闭文件
问:
r
模式打开文件和rb
模式打开文件有何区别?
答:因为直接以rb模式打开了文件 ,rb是指二进制模式,数据读到内存里直接是bytes格式,如果想内容,还需要手动decode,因此在文件打开阶段,不需要指定编码
问:假如你不知道你要处理的文件是什么编码可怎么办呢?
使用第三方模块chardet
获取最接近的文件的编码
import chardet
f = open('log',mode='rb')
data = f.read()
f.close()
result = chardet.detect(open('log',mode='rb').read())
# result = chardet.detect(data)
print(result)
输出:
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
循环文件
# -*- coding: utf-8 -*-
fr = open(file='致橡树.txt', mode='rb', encoding='gb2312')
for line in fr: # 这里可以直接用fr,而不需要用fr.read() 或者fr.readlines()
print(line)
fr.close()
或者
重要的用法,使用迭代器,用一行,读一行
# -*- coding: utf-8 -*-
"""对于大数据文件,要使用这种用法。"""
fr = open(file='致橡树.txt', mode='rb')
for line in fr: # 这里可以直接用fr,for循环内部把fr变为一个迭代器,用一行,读一行。就不需要用fr.read() 或者fr.readlines()
print(line.decode('gb2312'))
fr.close()
写文件
# -*- coding: utf-8 -*-
fw = open(file="测试写文件.txt", mode='w', encoding='utf-8')
fw.write("将内容写入到测试文件中")
fw.close()
file="测试写文件.txt" 表示文件路径
mode='w' 表示只写
encoding='utf-8' 将要写入的unicode字符串编码成utf-8格式
f.write(...) 表示写入内容,写入的内容是unicode字符串类型,内部会根据encoding转换为制定编码的 01101010101,即:字节类型
f.close()
二进制形式写文件
# -*- coding: utf-8 -*-
fw = open(file="测试二进制写文件.txt", mode='wb')
fw.write("将内容以二进制形式写到测试文件中".encode('utf-8'))
fw.close()
file="测试二进制写文件.txt" 表示文件路径
mode='wb' 表示只以二进制模式写
f.write(...) 表示写入内容,写入的内容必须字节类型,即:是某种编码格式的0101010;因为写入的内容是在内存中的,也就是unicode编码,要将其转换为字节类型,这里即utf-8编码保存到硬盘文件中。
f.close()
注意:
文件操作时,以 “w”或“wb” 模式打开,则只能写,并且在打开的同时会先将内容清空。
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
wb,写入时需要直接传入以某种编码的0100101,即:字节类型
w 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
追加内容到文件中末尾行
# -*- coding: utf-8 -*-
fa = open(file="致橡树.txt", mode='a', encoding='gb2312')
fa.write('\n 我很喜欢这首诗')
fa.close()
注意:
文件操作时,以 “a”或“ab” 模式打开,则只能追加,即:在原来内容的尾部追加内容
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
ab,写入时需要直接传入以某种编码的0100101,即:字节类型
a 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
读写模式
读写模式
f = open("兼职白领学生空姐模特护士联系方式.txt",'r+',encoding="gbk")
data = f.read() #可以读内容
print(data)
f.write("\nblack girl 河北 167 50 13542342233") #可以写
f.close()
但上面的内容写到哪个位置了呢?答案是追加到了最后面。
写读模式
f = open("兼职白领学生空姐模特护士联系方式.txt",'w+',encoding="gbk")
data = f.read()
print(data)
f.write("\nnewline 1哈哈")
f.write("\nnewline 2哈哈")
f.write("\nnewline 3哈哈")
f.write("\nnewline 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
只读一行,遇到\r or \n为止
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
判断文件是否可写
修改文件内容
思路一、
我们已经学了seek,现在告诉你,之所以内容会追加到最后面,是因为,文件一打开,要写的时候,光标会默认移到文件尾部,再开始写。 现在我想修改中间部分,是不是seek(中间位置)再写就可以了。
但是可能会将后面的内容覆盖掉,这是硬盘的存储原理导致的,当你把文件存到硬盘上,就在硬盘上划了一块空间,存数据,等你下次打开这个文件 ,seek到一个位置,每改一个字,就是把原来的覆盖掉,如果要插入,是不可能的,因为后面的数据在硬盘上不会整体向后移。所以就出现 当前这个情况 ,你想插入,却变成了会把旧内容覆盖掉。
问:但是文件如果特别大,比如5个GB,读到内存,就一下子吃掉了5GB内存,好费资源呀,有没有更好的办法呢?
如果不想占内存,只能用另外一种办法啦,就是边读边改, 什么意思? 不是不能改么?是不能改原文件 ,但你可以打开旧文件 的同时,生成一个新文件呀,边从旧的里面一行行的读,边往新的一行行写,遇到需要修改就改了再写到新文件 ,这样,在内存里一直只存一行内容。就不占内存了。 但这样也有一个缺点,就是虽然不占内存 ,但是占硬盘,每次修改,都要生成一份新文件,虽然改完后,可以把旧的覆盖掉,但在改的过程中,还是有2份数据 的。
占硬盘方式的文件修改代码示例:
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()
上面的代码,会生成一个修改后的新文件 ,原文件不动,若想覆盖原文件
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会覆盖原文件
flush方法同步将数据从缓存转移到磁盘
示例:实现进度条功能
import sys
import time
for i in range(100):
sys.stdout.write('*')
sys.stdout.flush() # flush的作用相对于拍照,拍一张冲洗一张。
time.sleep(0.2)
或者
import sys
import time
for i in range(100):
print('*', end='', flush=True) # print中的flush参数
time.sleep(0.2)
另外3种模式:r+、w+、a+
r+:读写模式,光标默认在起始位置,当需要写入的时候,光标自动移到最后
w+:写读模式,先清空原内容,再写入,也能够读取
a+:追加读模式,光标默认在最后位置,直接写入,也能够读取。
f = open('file','a')
print(f.tell())
f.close()
f = open('file','r+')
print(f.tell()) #0位置
print(f.readline()) #读取第一行
f.write('羊小羚')
print(f.tell())
f.seek(0) #光标移到0位置
print(f.readline()) #读取第一行
f.close()
修改文件内容
思路:由于数据存储机制的关系,我们只能把文件1中的内容读取出来,经过修改后,放到文件2中。
f2 = open('file2', 'w', encoding='utf8') # 写入的时候必须加utf8
f1 = open('file', 'r')
num = 0
for line in f1: # 迭代器
num += 1
if num == 5:
line = ''.join([line.strip(), '羊小羚\n']) # 里面就是对字符串进行操作了
f2.write(line)
f1.close()
f2.close()