13 December 2004
This post may be outdated due to it was written on 2004. The links may be broken. The code may be not working anymore. Leave comments if needed.

描述

很多人知道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是采用这模式不彻底的原因。


blog comments powered by Disqus