你可曾有过这般经历呢, 辛辛苦苦地在本子之上记了半页笔记, 借助代码予以“保存”处理, 接着再次打开时发现——人遭受别样状况了, 半页的字竟然全然都消失不见了。
这并非是灵异方面的事件, 这是在撰写文件期间, 极易被新手所忽视的一种情形表现, 具体为存在一些打开方式, 通常会先将旧的对应内容给擦拭干净, 之后才开始动笔书写。现今咱们针对“写入文件”这一行为事项, 逐环节彻头彻尾地梳理一番。待看完之后你就会清楚明白, 究竟是因何缘故, 导致有的人写出来的文件点开打开内容却是空的, 以及为何一句话能够“悄然无声”地将之前的内容全部删光的具体缘由。
打开文件,先表态度
处于其中时, 进行文件的读取与写入操作均需先实施“打开”行为, 所运用的便是open()。当执行打开动作之际,你必须告知它一桩事情: 你计划以怎样的方式来对待此文件。
在这里头, 最容易混淆弄错的东西是——读以及写, 针对“文件不存在”这种状况所持的态度是完全相反的。
读(r 模式):文件不存在?直接报错,绝不惯着你。
写(w 模式):文件不存在?没事,我现给你建一个。
因此呢, 撰写文件相较于阅读文件而言, 会“温柔”那么一点儿: 你并不需要预先就把文件给筹备妥当, 代码它自己能够去构建。然而温柔所处的另一面却是凶险万分, 接下来继续看。
W、A、X 三种写法,像不像三种人
把文件当作一本工作笔记本去想象, 于是乎, 这三种模式居然就是你翻开本子之后的三个态度, 没错就是这样!
W模式, 也就是写入加截断模式, 它的作用等同于我要进行重写操作, 并且会先把旧有的内容全部擦除, 代码一旦运行, 原来本子上所记录的东西会瞬间被清空, 只剩下你新写入的内容, 我运行了一遍, a.txt文件原本是一首包含四行的诗, 运行完W模式并写入你好之后, 再次打开这个文件就只剩下你好这两个字了。
故而, w模式运用起来可得万分小心, 你原本是想着添加些内容的, 然而, 最终却把先前的所有都给清除得一干二净了。
with open("a.txt", "wt", encoding="utf-8") as file: file.write("你好") # 原本四行诗 → 运行后只剩"你好"A模式(追加), 其情形等同于“我于最后一页接续书写, 而前面的任何一个字都维持原状没有变动”。无论本子先前记录了什么内容, “你好”定然安坐落于末尾之处。在日常状况当中, 极具可能性引发问题的行为便是“仅仅期望增添一行日志、添加一条记录”, 运用a模式最为稳妥。
with open("a.txt", "at", encoding="utf-8") as file: file.write("你好") # 在文件末尾追加,旧内容原封不动X模式(排他创建), 这一模式代表着如下情况, 即相当于这样一种规定, 仅有在这本子不存在的这种时候, 才会允许你去进行新建操作, 要是该本子已然存在, 便会直接予以拒绝。这实际上是一种保护机制, 当你原本打算去新建一个文件时, 而结果该文件名字却和别人的重了, 这种情况下, X模式就会立刻出现报错信息并拦住你, 以此来避免出现稀里糊涂就覆盖掉已有文件的状况。
with open("a.txt", "xt", encoding="utf-8") as file: file.write("你好") # 文件已存在 → 抛出 FileExistsError with open("demo.txt", "xt", encoding="utf-8") as file: file.write("你好") # 文件不存在 → 创建成功,内容写进去了缓冲区:为什么你"写了"却"没存上"
这是新手第二个大坑。
你觉得 file.write("你好") 一旦执行, 内容就进入文件之中了吗? 并非如此。十分“聪慧”: 它会先将内容暂时放置于内存里的一块缓冲区之内, 等到积攒足够或者你关闭文件之际,才切实写入到硬盘当中。
如下是一个格外直观的演示: 持续不断地写下“你好一”, 紧接着再 Write“你好二”, 随后程序蓄意地出现卡顿现象(睡眠相当长的时间)。在这个时刻去开启文件, 结果发现文件内部是空无一物的, 不存在任何内容。这是由于那两句内容仍旧处于缓冲区之中, 尚未成功落地。
import time with open("demo.txt", "wt", encoding="utf-8") as file: file.write("你好一") file.write("你好二") time.sleep(10000) # 程序卡在这 file.write("你好三") file.write("你好四") # 卡住时打开 demo.txt:空的,"你好一/二"还在缓冲区为何会这样进行设计呢, 是由于硬盘在读写方面速度较为缓慢, 而内存的速度挺快, 然而你每书写出一句话便去操作一次硬盘, 当书写一万句话时就进行一万次这样的折腾动作, 如此电脑会变得非常疲劳, 所以将其放置在缓冲区来积攒一批内容之后再进行书写, 这样能省事许多。
那我想立刻写进去怎么办?用 file.flush():
with open("demo.txt", "wt", encoding="utf-8") as file: file.write("你好一") file.write("你好二") file.flush() # 立刻把缓冲区内容写入磁盘 time.sleep(10000) file.write("你好三") file.write("你好四") # flush 后,"你好一/二"已经稳稳在文件里存在着一个隐藏的知识点, 当你在进行“点停止运行”这一操作的时候, 程序运转实际经历了close()这个程序动作, 并且close内部运作时会首先将缓冲区里的内容写入到文件当中。所以当你第一次出现“卡住时点停止”这种情况时, 内容反倒进入进去了。这并非是什么魔法, 而是close操作在帮你完成最后的收尾工作。
+ 号:又能读又能写
前面所提及的r仅仅只能用于读, w/a/x仅仅只能用于写。然而添加一个 + 号, 就转变成为了"更新模式", 读与写这两者都可行。但是必须要明确的是, + 号是不能够单独进行使用的, 务必要搭配一个主模式方可使用: rt+、wt+、xt+、at+。
这儿最为坑人的是文件指针, 你能够将其理解成“笔停留在本子的哪一个行数位置”, 以 r+ 方式打开它, 那支笔默认情况下待在起始部位, 一旦你进行书写, 便会从起始位置开始覆盖。
with open("a.txt", "rt+", encoding="utf-8") as file: file.write("你好") # a.txt 开头本是"血压之头",写完后变成"你好直头地" # 前两个字被"你好"覆盖想追加到末尾?先把笔挪到结尾:
with open("a.txt", "rt+", encoding="utf-8") as file: file.seek(0, 2) # 指针移到文件末尾 file.write("你好") # 这次才真正追加在后面中文的隐形陷阱
在seek(, )当中, 所指的是“字节数”, 并非“字数”, 英文里的一个字母, 占据1字节, 而中文在UTF-8这种编码格式下, 通常占据3字节。
我有过一回尝试: 打算从起始处进行偏移, 然而写下了偏移量2(当时误以为是两个字), 实际上仅仅移动了2个字节, 致使一个有着3个字节的中文“血”字从中间被截断, 文件随即报出编码错误、内容损坏的情况。正确的做法是: 在中文环境下偏移量按照字节来计算, 一个汉字就采用3;又或者最为稳妥的——直接seek(0, 2)移至末尾进行追加, 别去计算中间的位置。
记住, 在处于文本模式的情况下, 千万别是以“字数”当作偏移量进而去定位中文, 不然的话, 很容易出现把字切断, 以及把文件写坏这种情况。
最后一句大实话
只想加内容:用 a(追加),最不容易出事。
确定要覆盖重写:用 w,但先确认旧内容不要了。
怕名字冲突覆盖别人:用 x,存在就报错拦你。
又读又写:上 + 号,但时刻记住"笔停在哪"。
写好重要数据后想要马上落盘, 这时哟要记得flush() , 或者要保证close() 已经执行了。
文件操作看似简易, 实则陷阱尽藏于“想当然”当中。将这几种模式扎实记牢之举, 可使得删减之物大大减少。