python | 文件的IO操作你真的精通吗

文章探讨了Python3中处理文件时遇到的编码问题,如UTF-8和GBK编码下字符占用的字节数。详细解释了sys.getsizeof()和fp.tell()在计算字节大小时的区别。同时,介绍了文件的读写模式,如r,w,a,r+,w+,a+,以及它们在读写操作中的行为差异,特别是r+模式下的写入定位。文章还讨论了解决在文件头部插入文本的方法,涉及fp.seek()的使用。
摘要由CSDN通过智能技术生成

背景

在更新文件的时候,考虑到在文件头部加上 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,意为以文本模式读取,wa 亦如此。

  • 打开一个文件,只能进行读取操作,不能写;
  • 默认读取位置从文件头部开始。
  • 如果文件不存在,抛出异常。

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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值