文件目录操作

文件操作

文件准备:
致橡树.txt 编码格式为: gb2312

致橡树
我如果爱你---
  绝不像攀援的凌霄花,
  借你的高枝炫耀自己;
  我如果爱你---
  绝不学痴情的鸟儿,
  为绿荫重复单调的歌曲;
  也不止像泉源,
  常年送来清凉的慰藉;
  也不止像险峰,
  增加你的高度,衬托你的威仪。
  甚至日光,
  甚至春雨。
不,这些都还不够!
  我必须是你近旁的一株木棉,
  作为树的形象和你站在一起。
  根,紧握在地下;
  叶,相触在云里。
  每一阵风过,
  我们都互相致意,
  但没有人,
  听懂我们的言语。
  你有你的铜枝铁干,
  像刀,像剑,也像戟;
  我有我红硕的花朵,
  像沉重的叹息,
  又像英勇的火炬。
我们分担寒潮、风雷、霹雳;
  我们共享雾霭、流岚、虹霓。
  仿佛永远分离,
  却又终身相依。
  这才是伟大的爱情,
  坚贞就在这里:
  爱---
  不仅爱你伟岸的身躯,
  也爱你坚持的位置,
  足下的土地。

问题:文件在硬盘上是如何存储?
答:以某种编码格式的 “010101010101001” 【二进制的字节流】保存在硬盘上。这些编码例如utf-8gb2312gbk等等。

读取文件

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()
posted @ 2018-05-04 00:42  hehongjie  Views(387)  Comments(5Edit  收藏  举报