测试开发水货-python系列-被吐槽了

128 阅读5分钟

话说这天,群里有人提问

我的想法是:先random取个随机数n,然后读取这个文件,取n个字符,之后把这次取到的字符串从原文里删除。下次再读取就是上次的内容了。 实践一下吧,水货博主的小白写法:

import os
import random

def file_handler():
    with open(r"./tmp.txt", mode="r+", encoding="utf-8"as f:
        #随机取N个字符 然后做个切片,前半部分作为取值,清空文本内容,再把后半部分写回去
        n = random.randint(05)
        print("n是:",n)
        data = f.read()
        print(data)
        data1 = data[n:]
        print("我是分割线______")
        print(data1)
        f.truncate(0)
        f.write(data1)

第一次运行,效果良好。反正需求就是字面那个意思么,什么设计模式,什么代码优雅,不存在的:

n是:2
11111111
22222222
33333333
44444444
我是分割线______
111111
22222222
33333333
44444444

自我感觉尚可,然而再次运行时,出问题了,程序并没有像我想的那样从上次的位置进行分割:

n是:4
111111
22222222
33333333
44444444
我是分割线______
111111
22222222
33333333
44444444

而且更严重的是,随着运行次数的增加,电脑也变得越来越卡了,直到出现这样一行提示:

究竟是什么东西导致文件变得这么大,ide里看还是这几行字符串而已啊。我又用命令行执行了一下脚本,看起来还是没变化,

查了一下truncate这个函数的解释,最后一句话是重点,在大多数系统上,会把0作为额外填充,且返回新的文件。

truncate(size=None)
Resize the stream to the given size in bytes (or the current position
if size is not specified).  The current stream position isn’t changed.
This resizing can extend or reduce the current file size.  In case of
extension, the contents of the new file area depend on the platform
(on most systems, additional bytes are zero-filled).  The new file size is returned.

看来问题出在这里了,我每次执行,实际都写入了一堆/0在里面,拷到剪贴板里就一目了然了。

n是:2
11111111
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������22222222
33333333
44444444
我是分割线______
11111111
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������22222222
33333333
44444444

这些奇怪的占位符其实都是"\0"或者0x00填充的。至于为什么会这样就涉及到一个游标指针(seek)的概念了。我们在执行f.read()后,指针会默认在文本结尾的位置,此时执行truncate(0)会把原先的内容删除,但是指针的位置不会发生变化,而原来位置的内容会被\0填充,然后写入新文本的位置依然是原来的结尾。导致我每次运行时实际上切片切的都是被填充的这些0占用了,然后在此基础上复写,越来越多……然而这些内容又不会在IDE或者命令行里被打印出来,无形中踩了个坑。解决的办法很简单,就是声明一下这个游标指针的位置,在f.read()方法后,加一个seek(0),让指针回到文本开头,再执行truncate(),就不会出现这种文件越来越大的问题了。

当然,还有更简单的办法

来自我无敌卷魔大王的方法:

import os
import random

def file_handler():
    with open(r"./tmp.txt", mode="r", encoding="utf-8"as f:
        #随机取N个字符
        n = random.randint(05)
        while True:
            data = f.read(n)
            if not data:
                print("读完了")
                break
            print("data是:",data)


简单,且不伤害原本的文件,不像本菜的野蛮切割法那样破坏了原本的文件结构。当然我也可以通过新建文件的方式把原文件保留,取的内容存到新文件里,这是后话了。通过研究这个问题,重温了python操作文本的一些基础知识点。比如,读取文件的几种方式:

  • r  只能读 如果文件不存在则报错

  • r+ 可读可写 如果文件不存在则报错 会覆盖原内容

  • w 只能写 如果文件不存在则创建 会覆盖原内容

  • w+ 可读可写 如果文件不存在则创建 会覆盖原内容

  • a 只能写 创建 不会覆盖原内容,在文本结尾追加写

  • a+ 可读可写 创建 不会覆盖原内容,在文本结尾追加写

还有一个潜在的知识点,以前没关注过的:

def print(self, *args, sep=' ', end='\n', file=None): # known special case of print
    """
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.
    """
    pass

就是这个print的用法,默认是在结尾加了换行符\n的!在我们写文件的时候就没必要特意去加换行符操作了,或者在很多情况下需要对换行符进行额外处理,通过Strip()等等。这些东西平时可能注意不到,一旦被问到可能就会懵。

最后,还是要接受批评

学习吧,知识盲区太多了。唯有坚持学习,才是内卷时代生存下去的动力。 如果觉得有收获,欢迎关注点赞互踩~