Py002_01_03文件操作

python文件处理

读取

1
2
3
f = open(file='xxx.txt',mode='r',encoding='utf-8')
data = f.read()
f.close()
  • file 是目标文件
  • mode 代表文件操作模式 r意思是只读
  • encoding 是以xx编码读取文件

注意:

  • 读取文件的编码方式和文件存储时的编码要一致
  • 如果不传递encoding,在python3里默认utf-8
1
如果文件是gbk的形式存储的,那么读取时如果encoding='utf-8'就会报错

二进制模式

如果我不知道文件的编码也想把内容读取进来,你可以使用二进制模式

把这段内容直接以二进制的形式读取进来,不进行按照它的编码方式编码

  • mode=rb

主要用来视频/图片/文件传输

1
2
3
f = open(file='xxx.txt',mode='rb',encoding='utf-8')
data = f.read()
f.close()

智能检测编码的工具

需要手动安装工具包

1
pip install chardet
1
2
3
4
5
6
f = open(file='xxx.txt',mode='rb')
data = f.read()
f.close()
print(chardet.detect(data))
# 输出一个字典显示 改二进制对应的编码
{'encoding': 'gb2312', 'confidence': 0,8321312312, 'language': 'chinese'}

read()的缺陷

一次性把文件读取到内存,如果文件3G呢?

循环文件(一次读一部分)

1
2
3
4
5
6
f = open(file='xxx.txt',mode='r',encoding='utf-8')

for line in f:
print(line)

f.close()

写文件

普通写入

1
2
3
f = open(file='xxx.txt',mode='w',encoding='gbk')
f.write("你好!!!")
f.close()

二进制模式写入

1
2
3
f = open(file='xxx.txt',mode='wb')
f.write("你好!!!".encode()) # encode默认utf-8
f.close()

mode=’w’每次都会创建一个新文件,如果已经存在就覆盖了

同一文件不想每次都覆盖==>追加模式

1
2
3
f = open(file='xxx.txt',mode='ab')
f.write("你好!!!".encode()) # encode默认utf-8
f.close()

混合操作文件

既能写又能读的模式

  • r+ 读写模式—>先读后写
  • w+ 写读模式—>先创建然后读取(先把文件覆盖,然后读取新写入的内容)(几乎用不到)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
假设文件里有内容
f = open('info.txt','r+',encoding='gbk')
print(f.read())
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
print('````````````````')
print(f.read()) # 为什么这次的内容是空的
f.close()

详解:
文件读取的时候会有一个光标
1. 第一次read()的时候全部读出来 光标移动到读取内容末尾
2. write的时候 光标移动到写入内容的后面
3. 第二次read()的时候 由于光标后面没有任何东西,所以为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
假设文件里有内容
f = open('info.txt','w+',encoding='gbk')
print(f.read()) # 为什么这次的内容是空的
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
f.write('\n何大宝 哈哈 11111111111')
print('````````````````')
print(f.read()) # 为什么这次的内容也是空的
f.close()

详解:
0. 写读模式打开--》创建这个文件如果已经存在就覆盖了
1. 第一次read() 文件是新建的本来就是空的
2. write的时候 光标移动到写入内容的后面
3. 第二次read()的时候 由于光标后面没有任何东西,所以为空

最没用模式---------几乎用不到

文件操作其他方法

  • fileno() 网络编程会用,返回一个数字
  • flush() 强制把文件buffer里(内存里)的内容写入到文件里
  • readable() 是否可读
  • readline() 每次读一行
  • seek() 设置光标的位置 (下次读的时候从这个位置开始)
  • tell() 返回光标的位置
  • seekable() 判断文件是否可以seek操作 比如终端命令行也是一个文件就不允许seek
  • writeable() 判断是否可写
  • truncate() 从指定位置(光标)截断文件

flush功能

1
2
3
4
5
6
7
8
9
10
f = open('info.txt','w')
f.write('aa')
f.write('aa')
f.write('aa')

# ....只要你不 f.close() 写入的内容就在 内存里也就是缓存里
# 因为频繁写入文件的话很耗性能
# flush是什么鬼呢?
# 如果你写的内容非常重要 必须确保每次都write的时候写入到文件里,就用flush
f.flush() # 此时就算你不f.close()也可以在存盘
tell和seek的坑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 假设文件内容是 'hello world'

f = open('info.txt','r',encoding='gbk')
f.tell() # 0
f.seek(3)
f.readline() # lo world
------------------------------------------------
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='gbk')
f.seek(3)
f.readline() # 报错 decode error
-----------------------------------------------
# 验证为什么报错
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='gbk')
f.seek(0)
f.readline() # '路飞学城' 没问题啊???
-----------------------------------------------
# 验证为什么报错
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='gbk')
f.seek(2)
f.readline() # 报错 decode error 为啥还报错!!!
-----------------------------------------------
# 验证为什么报错
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='gbk')
f.read(1) # 路
f.tell() # 2 读了一个汉字 为什么走俩,我猜可能是因为gbk里汉字占俩字节
f.read() # 飞学城
-----------------------------------------------
# 验证utf-8汉字是否会 移动三个光标?
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='utf-8')
f.read(1) # 路
f.tell() # 3 事实证明,结论是对的
f.read() # 飞学城

结论

  • 无论seek 还是 tell 它们找的都是字节
  • read(1) 是读一个字符

truncate

1
2
3
4
5
6
7
8
9
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='utf-8')
f.truncate()
f.flush() # 此时文件是空的
----------------------------------
# 假设文件内容是 '路飞学城'
f = open('info.txt','r',encoding='utf-8')
f.truncate(6)
f.flush() # 此时文件是 '路飞' 代表从头开始截取6个字节

文件修改

实际是不能直接像word那样修改文件内容的,因为word是把所有内容读到内存里。

用光标也不靠谱:因为 比如seek的时候 张三 变成 哈尔滨 这样张三后面的内容不会让出位置,而是覆盖。

变通方式

  • 写道磁盘上
  • 写道内存里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os

f_name = "info.txt"
f_new_name = "%s.new"%f_name

old_str = '张三'
new_str = '哈尔滨'

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:
line = line.replace(old_str,new_str)

f_new.write(line)

f.close()
f_new.close()

os.rename(f_new_name,f_name)