一, 二进制处理文件
通过前面的说明, 我们知道'b'模式是通过字节的形式来读写文件, 但是要理解一点的是, 这种模式只是在内部处理的时候是字节, 但是我们打开文件看到的还是字符串的形式, 而不是一堆字节. 还有一点需要了解的是, 在 Linux 系统里面,'b'模式是没有作用的, 因为 Linux 理念的是一切皆文件, 他本来所有的文件都是通过字节操作的, 所以如果要跨平台处理文件, 需要用到'b'模式.
1.'rb'模式
下面就来演示'b'模式读取一个 py 文件, py 文件的内容如下:
- hello
- 11111
- 22222
- 33333
根据前面的内容, 我们一般这么写:
- f=open('test11.py','rb',encoding='utf-8') #b 的方式不能指定编码
- data=f.read()
- print(data)
- f.close()
但是运行后会发现报错, ValueError: binary mode doesn't take an encoding argument,b 的方式不能指定编码, 为什么不能指定? 因为你文件存在硬盘的方式就是一堆二进制, 之所以你能看到字符串, 是因为你是用应用程序打开的文件, 应用程序有不同的编码方式, 所以如果你是'r'模式去读, 要区分编码, 如果是'rb'模式去读, 读的是原生的二进制, 就不用应用程序编码了, 所以会报错.
所以正确的写法是这样的:
- f=open('test11.py','rb') #b 的方式不能指定编码
- data=f.read()
- print(data)
- f.close()
拿到的结果是:
b'hello\r\n11111\r\n22222\r\n33333'
前面这个 b, 就说面这一个字节内容, 如果在源文件加一列中文, 结果会是:
b'hello\r\n11111\r\n22222\r\n33333\r\n\xe4\xbd\xa0\xe5\xa5\xbd'
如果你想看到你原本写的字符串, 就要对结果进行解码 (对应你写入字符串时的编码, 我这里写入时为'utf-8'编码), 也就是 decode:
- f=open('test11.py','rb') #b 的方式不能指定编码
- data=f.read()
- #'字符串'---------encode---------》bytes
- #bytes---------decode---------》'字符串'
- # print(data)
- print(data.decode('utf-8'))
- f.close()
这时看到的结果就是:
- hello
- 11111
- 22222
- 33333
你好
这里有一个小的补充: Windows 里面的回车 \ r\n,Linux 和 unix 的回车则直接是 \ n, 所以跨系统传文件打开后看到的会有一些偏差, 原因可能就是这个.
2.'wb'模式
读文件我们会了, 写文件也是一样的, 我们写入也要写入 bytes 形式的, 字符串转换成字节可以通过 bytes() 函数实现.
- f=open('test22.py','wb') #b 的方式不能指定编码
- f.write(bytes('1111\n',encoding='utf-8'))
- f.close()
运行完之后我们拿到一个新文件, 里面的内容就是 1111, 这里面写中文也是可以的. 字符串转字节是一种编码过程, 那我们是不是可以用 encode 方法呢?
- f=open('test22.py','wb') #b 的方式不能指定编码
- # f.write(bytes('你好 \ n',encoding='utf-8'))
- f.write('你好'.encode('utf-8'))
- f.close()
运行完之后, 结果同样 OK.'ab'模式用法就不多说了, 和前面的 a 类似. 现在有一个疑问, 为什么我们要用 b 模式来处理文件? 那我用 r/w/a 也可以完成上面的操作. 原因有两点:
1. 文件不仅仅只有文本这一种形式, 还有图片视频等, 这些非文本形式你用 r/w/a 处理不了;
2. 就是之前说过的, 你需要跨平台处理文件的时候, 不管你什么平台, 你最后处理的都是一堆二进制;
二, 文件操作的其他方法
操作文件的方法除了之前说 read,readlines,readline,write 等, 还有其他方法可以了解一下:
- 1.f.encoding (如果文件打开模式为 b, 则没有该属性)
- f=open('a.txt','r+',encoding='gb2312')
- print(f.encoding)
- f.close()
- >>>gb2312
这里拿到的文件打开的编码, 切记, 和源文件以什么方式存放到硬盘的方式无关. 无法解决你因为不知道源文件编码时, 打开文件乱码的问题. 不过这种情况很少见, 大部分情况你都是知道源文件编码的, 如果真的遇到这种情况, 就可以用一种 encoding='latin-1'的编码去打开, 这种编码兼容了大部分的编码方式.
2.f.flush() (立刻将文件内容从内存刷到硬盘)
当你通过 open 打开一个文件的时候, 你写的东西其实都是内存里面, 并没有保存在硬盘中, 你不保存, 这些数据就一直在内存中, 假如关闭或断电了就没了. 可是你会发现这种情况在 pycharm 里面, 你没有保存, 文件内容断电之后也还存在, 这是因为这些应用程序每隔几秒就自动帮你保存一次, 这种保存的方法就是 flush(), 你可以在 cmd 里面实验这种情况.
3.f.tell() (返回文件光标所在的位置)
现在有一个文件, 就写了一行 hello, 看看 tell 返回什么
- f=open('b.txt','r',encoding='utf-8')
- print(f.tell())
- f.readline()
- print(f.tell())
- >>>0
- >>>5
返回的就是当前光标的位置, 注意当有多行时, 会加上回车的两个字节.
4.python 做的好事!
上面我们说过, Windows 的回车是 \ r\n, 下面看一下:
- f=open('b.txt','r',encoding='utf-8')
- print(f.readlines())
- f.close()
- >>>['hello\n', '11111']
怎么我打印出来只有 \ n 呢? 这就是 python 帮你处理过的结果, 想要拿到真正的结果, 还要加上一个 newline=''f=open('b.txt','r+',encoding='utf-8',newline='') #读取文件中真正的换行符号
- print(f.readlines())
- f.close()
- >>>['hello\r\n', '11111']
这样拿到的才是真实的结果, 不过这没什么用, 了解一下就行.
5.f.seek()
在讲这个之前, 要先了解一下光标移动的两个点:
一: read(3):
1. 文件打开方式为文本模式时, 代表读取 3 个字符
2. 文件打开方式为 b 模式时, 代表读取 3 个字节
二: 其余的文件内光标移动都是以字节为单位如 seek,tell,truncate
- f=open('b.txt','r+',encoding='utf-8')
- f.seek(1)
- print(f.tell())
- f.seek(3)
- print(f.tell())
- >>>1
- >>>3
这就很容易理解了, 但是如果你是中文开头的, 一个中文是 3 个字节, 你 seek(1) 的话就要报错了. 还有一点就是, 这里的 seek 都是以 0 为基准进行移动的. 这种是默认情况, seek 有三种移动方式 0,1,2, 其中 1 和 2 必须在 b 模式下进行, 但无论哪种模式, 都是以 bytes 为单位移动的. 建议试一下 read 和 seek 的区别.
- f=open('b.txt','rb')
- print(f.tell())
- f.seek(10,1)
- print(f.tell())
- f.seek(3,1)
- print(f.tell())
- >>>0
- >>>10
- >>>13
从上可知, seek 模式改为 1, 就变为相对位置了. 再改为 2 试试:
- f=open('b.txt','rb')
- print(f.tell())
- f.seek(-5,2)
- print(f.read())
- print(f.tell())
- f.seek(3,1)
- print(f.tell())
- >>>0
- >>>b'11111'
- >>>12
- >>>15
模式 2 就是倒着 seek, 倒着移动的话, 里面的参数也要写成负数, 注意了. 那么这个 seek 有什么用呢, 就要在实际场景中去思考了.
6. f.truncate([size]) 把文件裁成规定的大小
如果 size 比文件大小还大, 这个会根据系统的不同可能不改变文件, 可能补充 0 进去, 或者补充一些随机的内容. truncate(10) 就是从开头截取到第 10 个字节. 所以文件的打开方式必须可写, 但是不能用 w 或 w + 等方式打开, 因为那样直接清空文件了, 所以 truncate 要在 r + 或 a 或 a + 等模式下进行.
其他方法很少用到, 了解一下就行了.
来源: https://www.cnblogs.com/pengfy/p/10820557.html