Python中truncate(0)之后文件中的垃圾

发布于 2021-01-29 14:57:03

假设有一个test.txt包含字符串的文件'test'

现在,考虑以下Python代码:

f = open('test', 'r+')
f.read()
f.truncate(0)
f.write('passed')
f.flush();

现在,我希望现在test.txt包含它'passed',但是另外还有一些奇怪的符号!

更新:截断后刷新无济于事。

关注者
0
被浏览
65
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    这是因为截断不会更改流的位置。

    当你read()的文件,你移动位置到结束。因此,连续的writes将从该位置写入文件。
    但是,当您调用时flush(),似乎不仅尝试将缓冲区写入文件,而且还会进行一些错误检查并修复当前文件位置。在Flush()之后调用时truncate(0),将不写入任何内容(缓冲区为空),然后检查文件大小并将该位置放置在第一个适用的位置(即0)。

    更新

    Python的文件功能不仅是与C标准库等效物的包装,而且了解C函数有助于更准确地了解正在发生的事情。

    ftruncate手册页中

    调用ftruncate()不会修改seek指针的值。

    笨拙的手册页

    如果流指向输入了最新操作的输入流或更新流,则在可搜索且尚未到达文件末尾的情况下刷新该流。刷新输入流将丢弃所有缓冲的输入,并调整文件指针,以使下一个输入操作在最后一次读取后访问该字节。

    这意味着如果您放置flushtruncate它之前没有效果。我检查了,是这样。

    但是为了flush追求truncate

    如果流指向未输入最新操作的输出流或更新流,则fflush()会将该流的任何未写入数据写入文件,并标记基础文件的st_ctime和st_mtime字段进行更新。

    手册页在解释未输入最后操作的输出流时没有提及查找指针。(这是我们的最后一个操作是truncate

    更新2

    我在python源代码中找到了一些东西: Python-3.2.2\Modules\_io\fileio.c:837

    #ifdef HAVE_FTRUNCATE
    static PyObject *
    fileio_truncate(fileio *self, PyObject *args)
    {
        PyObject *posobj = NULL; /* the new size wanted by the user */
    #ifndef MS_WINDOWS
        Py_off_t pos;
    #endif
    
    ...
    
    #ifdef MS_WINDOWS
        /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
           so don't even try using it. */
        {
            PyObject *oldposobj, *tempposobj;
            HANDLE hFile;
    
    ////// THIS LINE //////////////////////////////////////////////////////////////
            /* we save the file pointer position */
            oldposobj = portable_lseek(fd, NULL, 1);
            if (oldposobj == NULL) {
                Py_DECREF(posobj);
                return NULL;
            }
    
            /* we then move to the truncation position */
            ...
    
            /* Truncate.  Note that this may grow the file! */
            ...
    
    ////// AND THIS LINE //////////////////////////////////////////////////////////
            /* we restore the file pointer position in any case */
            tempposobj = portable_lseek(fd, oldposobj, 0);
            Py_DECREF(oldposobj);
            if (tempposobj == NULL) {
                Py_DECREF(posobj);
                return NULL;
            }
            Py_DECREF(tempposobj);
        }
    #else
    
    ...
    
    #endif /* HAVE_FTRUNCATE */
    

    查看我指示的两行(///// This Line /////)。如果您的平台是Windows,则它将保存位置并在截断后将其返回。

    令我惊讶的是,flushPython
    3.2.2函数中的大多数函数什么都不做,或者根本没有调用fflushC函数。3.2.2截短部分也没有记录。但是,我确实在Python
    2.7.2源中发现了一些有趣的东西。首先,我Python-2.7.2\Objects\fileobject.c:812truncate实现中发现了这一点:

     /* Get current file position.  If the file happens to be open for
     * update and the last operation was an input operation, C doesn't
     * define what the later fflush() will do, but we promise truncate()
     * won't change the current position (and fflush() *does* change it
     * then at least on Windows).  The easiest thing is to capture
     * current pos now and seek back to it at the end.
     */
    

    因此,总而言之,我认为这是完全依赖平台的事情。我检查了适用于Windows x64的默认Python 3.2.2,并获得了与您相同的结果。不知道在*
    nixes上会发生什么。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看