2012-01-20 41 views
5

假定有一個文件test.txt包含字符串'test'Python中截斷(0)後的文件中的垃圾

現在,請考慮以下Python代碼:

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

現在我希望test.txt包含'passed'現在,但也有一些額外奇怪的符號!

更新:truncate後刷新沒有幫助。

+1

據我所知,運行'truncate(0)'只需將文件大小設置爲0字節,並將文件指針移回到緩衝區的開始。在刷新之前寫入流時,緩衝區的狀態是未定義的。在刷新時,可能會從該緩衝區中顯示垃圾數據。我應用的一般規則是,只要您嘗試*刪除*任何內容,或者在繼續之前需要確保文件處於特定狀態時,您都應該刷新。 – Polynomial

+0

關閉文件怎麼辦? – joaquin

+0

@Polynomial flush也不起作用,我誤會了...... – Beginner

回答

4

這是因爲truncate不改變流的位置。

當您將文件read()移動到最後。因此,連續的write將從該位置寫入文件。 但是,當您撥打flush()時,它似乎不僅嘗試將緩衝區寫入文件,而且還會執行一些錯誤檢查並修復當前文件位置。在truncate(0)之後調用Flush()時,不寫任何內容(緩衝區爲空),然後檢查文件大小並將該位置置於第一個適用位置(即0)。

UPDATE

Python的文件的功能不只是圍繞着C標準庫等價的包裝,但我們知道的C函數有助於瞭解什麼是更精確地發生。

ftruncate man page

尋道指針的值不被呼叫改性與ftruncate()。

fflush man page

如果流指向一個輸入流或其中最近一次操作是輸入的更新流,該流被刷新,如果它是可搜索,是不是已經在年底-of文件。刷新輸入流會丟棄任何緩衝的輸入並調整文件指針,以便在最後一次讀取後下一個輸入操作訪問該字節。

這意味着如果您在之前truncate它沒有效果。我查了一下,結果如此。

但是用於使flushtruncate

如果流點到輸出流或其中的最近操作不是輸入的更新流,fflush()導致該流是任何未寫入數據寫入文件,並且底層文件的st_ctime和st_mtime字段被標記爲更新。

當解釋輸出流時沒有輸入上一個操作時,手冊頁沒有提到seek指針。 (在這裏我們最後的操作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,那麼它將保存位置並在截斷後將其返回。

令我吃驚的是,Python 3.2.2函數中的大多數flush函數或者什麼也沒做,或者根本沒有調用fflush C函數。 3.2.2截斷部分也很沒有記錄。但是,我確實在Python 2.7.2源代碼中找到了一些有趣的東西。首先,我在truncate實施來找到這Python-2.7.2\Objects\fileobject.c:812

/* 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上發生了什麼。

+0

看到我的更新,但我認爲你是在正確的軌道上。我可能應該尋求額外的零。 - 是截斷後尋求0的幫助!那麼截斷的真正目的是什麼? – Beginner

+0

我的操作系統是Linux,所以我懷疑在其他POSIX兼容系統上的行爲是一樣的。 – Beginner

-1

這取決於。如果你想保持文件打開並訪問它而不關閉它,那麼刷新將強制寫入文件。如果您在沖洗後立即關閉文件,那麼您不需要它,因爲關閉會沖洗你。這是我對docs的瞭解

+1

我想清除文件...並且不,我不想重新打開它。 – Beginner

1

截斷不會更改文件位置。

還要注意的是,即使文件在讀取和寫入中打開,您也不能在兩種操作類型之間切換(例如,需要查找操作才能從讀取轉換爲寫入或反之亦然)。

0

我期待下面是你的意思是寫代碼:

open('test.txt').read() 
open('test.txt', 'w').write('passed') 
0

是啊,這是真的,truncate()不動的位置,但他說,很簡單的死亡:

f.read() 
f.seek(0) 
f.truncate(0) 
f.close() 

這是完美的工作;)