背景
在更新文件的时候,考虑到在文件头部加上 version 及 change 之类的东西,以此来区分文件的异同并且能保存旧文件。但在实际操作中遇到了以下问题。
utf-8
下一个汉字占2个字节,包括各种标点符号、空格、换行符;gbk
下一个汉字占2个字节,标点符号没验证不知道。- 在 python3 中用
sys.getsizeof()
计算文本的所占字节大小,是根据编码方式计算字节大小的。如果文本包含汉字,减去74是真实大小,如果文本是纯英文, 减去49是真实大小,这个是由于 UTF-8 BOM 头存在机制的原因。 - 在 python3 文件指针
fp.tell()
中,一个汉字及中文标点符号是按3字节来算的,空格为一个字节,换行符为两个字节,所以写偏移量的时候要注意。
python中的 IO 操作
一个简单的文本读取如下
with open(r'.\test.txt', 'r', encoding='utf-8') as fp:
print(fp.read())
01 f.seek()
@abstractmethod
def seek(self, offset: int, whence: int = 0) -> int:
pass
offset
:从whence
开始,需要多少偏移量(必填参数),也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始;whence
:从哪个位置开始偏移(可选参数),默认值为 0。给 offset 定义一个参数,表示要从哪个位置开始偏移;0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。
02 fp.tell()
@abstractmethod
def tell(self) -> int:
pass
文件读取模式
python 中给出了多个文件读取模式符,但每个意思都有一些区别,以及各自的应用场景,为此我们需要理清各个字符所表达的含义。
类型 | 含义 |
---|---|
r | 只读 |
x | 只写 |
w | 只写 |
a | 追写 |
r+ | 可读可写 |
w+ | 可读可写 |
a+ | 可读可写 |
具体区别
r
rt
的简写,即 r
= rt
,意为以文本模式读取,w
、a
亦如此。
- 打开一个文件,只能进行读取操作,不能写;
- 默认读取位置从文件头部开始。
- 如果文件不存在,抛出异常。
w
- 打开一个文件,只能进行写操作,不能读;
- 默认写入位置从文件头部开始;
- 如果文件存在,则直接开始写,覆盖原内容;
- 如果文件不存在,则创建这个文件然后开始写。
x
- 打开一个文件,只能进行写操作,不能读;
- 默认写入位置从文件头部开始;
- 如果文件存在,抛出异常;
- 如果文件不存在,则先创建这个文件,然后开始写。
a
- 打开一个文件,只能进行写操作,不能读;
- 默认写入位置从文件尾部开始;
- 如果文件不存在,则先创建这个文件,然后开始追加写。
r+
- 打开一个文件,进行读写操作;
- 默认写入位置从文件头部开始;
- 如果文件不存在,抛出异常。
- 如果写之前进行了读操作(读到哪里文件指针就指到哪里),无论读取多少内容,写入始终追加到文件末尾,我以为会是从文件指针后开始写,但是并没有,猜测写python源码的可能做了保护,考虑到在读的时候如果文件文件只读了一部分,那么中途写必将会覆盖原内容,而此时还有原内容未读,这时候就产生了冲突,所以在此模式下不管读取全部内容还是部分,写的时候强制将文件指针移动到文件末尾。;
- 如果写之前未进行读操作,则从头开始写入内容,并覆盖原内容,覆盖原内容的长度为写入内容的长度,如果写入内容与原内容语言不一致这时候坑就来了,比如写入是英文,原内容为中文,原内容为
先生你好
(占12个字节),写入内容为hello
(占个5字节),「hello」不能够完全覆盖「中国你」,此时解释器就会抛出 UnicodeDecodeError。如果语言一致则不存在这个问题。 - 如果是先写后读,那么读取位置从写完内容后文件指针位置开始读取。
w+
- 打开一个文件,进行读写操作;
- 默认写入位置从文件头部开始;
- 如果文件不存在,则先创建这个文件。
- 写的优先级大于读
- 如果先读后写,那么读到的内容为空,因为他会先清空原文本,再开始重写整个文件。
- 如果先写后读,那么读到的内容也为空,因为写完后文件指针已处于文件末尾,此时读取为空。如果要读,那么需要通过
fp.seek()
改变文件指针指向。
a+
- 打开一个文件,进行读写操作;
- 默认写入位置从文件尾部开始;
- 如果文件不存在,则先创建这个文件。
- 如果先读后写,那么读到的内容为空,因为a 模式文件初始指针指向在文件末尾,对于写,内容直接追加到文件末尾。
- 如果先写后读,对于写,内容直接追加到文件末尾。对于读,读到的内容也为空,因为写完后文件指针已处于文件末尾,此时读取为空。如果要读,那么需要通过
fp.seek()
改变文件指针指向。
应用场景
类型 | 应用场景 |
---|---|
r | 只需要进行读文件的时候 |
x | 只需要进行创建文件的时候 |
w | 只需要进行读文件的时候 |
a | 只需要在文件后追加内容的时候 |
r+ | 读、覆写、先读后追加、插入 |
w+ | 覆写、创建文件、插入 |
a+ | 追加、创建文件、插入 |
实际上有 +
的模式都可以利用改变文件指针的方式来进行读、覆写、追加、插入,但还是建议按官方使用规范来。
解决
那么如何插入文本到文件头部?
with open('test.txt', 'r+') as fp:
insert_content = "insert_words"
content = fp.read()
fp.seek(0, 0) # 将文件指针移动到文件头部
fp.write('insert_content\n'+content)