[轉帖]關于open的"+<"模式

作者:非四。
地址:http://www.1313s.com/f/open.html

描述

很多人知道open除了<, >, >>模式外還有其他模式如"+<", 但有些人不太會用,或者說錯誤的用了"+<"模式。
一般我們更新文件(需要讀取原内容文件)時,都先open<讀取再對内容進行變化,最後open>寫入。
而用+<加seek可以實現文件的讀寫更新,不用兩次open,從而提高效率。
但值得注意的是:如果不用truncate,文件會出現你所不想要的後果。而另人遺憾的是書中說truncate不是所有平台都支持的。不過就我測試而言,Win2000/XP+Linux下都是可以的。
一切用實驗來說話。具體原理可以參考perldoc或書籍。

實驗

實驗材料:程序目錄下有一文本文件1.txt, 裡面的内容為簡單的“123456789”。
實驗一:簡單的計數器增加
原代碼:


open(FH,"1.txt");
flock(FH, 1);
my $count = <FH>;
close(FH);
$count++;
open(FH,">1.txt");
flock(FH, 2);
print FH $count;
close(FH);

改寫後的代碼:

open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
seek(FH,0,0);
print FH ++$count;
close(FH);


簡單的從兩次open轉化為"+<"模式就是這個樣子。

實驗二:不用truncate的不可意料錯誤。

這種錯誤發生在後來寫入的長度小于原文件的長度。
比如我們的任務是去掉1.txt中所有的1。如下代碼是錯誤的,出現的結果不是我們想要的。

open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
$count =~ s/1//sg;
seek(FH,0,0);
print FH $count;
close(FH);

我們所要的結果是23456789,而實際運行後的結果為234567899。後面多了一個9。
"+<"模式是覆蓋模式,你後來輸入的長度隻覆蓋那麼長,如果原來文件的長度比輸入的長度長,後面将會保留。
正确的代碼為:

open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
$count =~ s/1//sg;
seek(FH,0,0);
truncate(FH, 0);
print FH $count;
close(FH);

seek到文件頭後将文件清空,再輸入改變後的值。
當然有時候是不需要truncate的,看具體的要求具體分析。比如我們就要覆蓋那麼長後面的就要保留。
比如最上面的$count就不需要,因為加後的count不會小于原來的長度。

題外話

在寫本文之前,我在看LeoBBSx的最新版本代碼。LeoBBSx有用了+<模式(雖然不是很徹底,隻部分采用),但遺憾的是沒有truncate。
我沒在本地做測試看它會不會出錯,但我總覺得奇怪,它用這個模式的時候難道改變後的值都長于原來的值?
或許不用truncate是采用這模式不徹底的原因。