为什么重复打开、追加和关闭文本文件会导致行以相反的顺序写入?

pla*_*ect 6 perl

为什么以下 perl 代码:

for(open(my $fh, ">>", "test.txt")){
    print $fh "one\n";
}
for(open(my $fh, ">>", "test.txt")){
    print $fh "two\n";
}
for(open(my $fh, ">>", "test.txt")){
    print $fh "three\n";
}
Run Code Online (Sandbox Code Playgroud)

将以下内容写入 test.txt?

three
two
one
Run Code Online (Sandbox Code Playgroud)

为什么顺序颠倒了?我的理解是每个for块会在块退出时自动关闭文件。这不应该导致 perl 在下一个块开始之前刷新任何缓冲区吗?我希望这段代码可以打开文件,写一行,关闭文件,然后再重复所有这些步骤两次。我错过了什么?

我用 Perl 5.26.1 测试了这个,在 Ubuntu 18.04.3 上运行。

(是的,我知道只需将所有print语句放在同一个块中,我就可以轻松地以正确的顺序编写行。这不是这里的问题。我想了解为什么会发生这种行为。)

对于奖励奇怪,当我运行以下代码时:

for my $val (qw/ one two three /) {
    for(open(my $fh, ">>", "test.txt")){
        print $fh "$val\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

它给了我以下输出:

one
two
three
Run Code Online (Sandbox Code Playgroud)

这段代码看起来应该与之前的代码在功能上相同。为什么它的行为不同?

ike*_*ami 8

这只是一个复杂的版本

open(my $fh, ">>", "test.txt");
print $fh "one\n";
open(my $fh, ">>", "test.txt");
print $fh "two\n";
open(my $fh, ">>", "test.txt");
print $fh "three\n";
Run Code Online (Sandbox Code Playgroud)

让我们做些小改动,让事情更易读,更容易讨论:

open(my $fh1, ">>", "test.txt");
print $fh1 "one\n";
open(my $fh2, ">>", "test.txt");
print $fh2 "two\n";
open(my $fh3, ">>", "test.txt");
print $fh3 "three\n";
Run Code Online (Sandbox Code Playgroud)

这是等效的,因为每个都my创建了一个新变量。

那么发生了什么?

open(my $fh1, ">>", "test.txt");  # You create a file handle.
print $fh1 "one\n";               # You write to the file handle's buffer.
open(my $fh2, ">>", "test.txt");  # You create a file handle.
print $fh2 "two\n";               # You write to the file handle's buffer.
open(my $fh3, ">>", "test.txt");  # You create a file handle.
print $fh3 "three\n";             # You write to the file handle's buffer.
# Implicit close($fh3);           # You close the file handle, flushing its buffer.
# Implicit close($fh2);           # You close the file handle, flushing its buffer.
# Implicit close($fh1);           # You close the file handle, flushing its buffer.
Run Code Online (Sandbox Code Playgroud)

由于 Perl 不保证销毁变量的顺序,因此您可以轻松地以任何顺序获得输出。

解决方案是在打印到手柄后冲洗它们($fh->flush;、 或$fh->autoflush(1);),或者提前关闭手柄。

{ open(my $fh, ">>", "test.txt");  print $fh "one\n";   }
{ open(my $fh, ">>", "test.txt");  print $fh "two\n";   }
{ open(my $fh, ">>", "test.txt");  print $fh "three\n"; }
Run Code Online (Sandbox Code Playgroud)

  • 回复“*为什么 $|=1 不起作用?*”,`$| = 1;` 如果您选择正确的句柄,则确实有效。请记住,`$| = 1;`为*当前选定的句柄*设置自动刷新。 (2认同)
  • 关于“*您能否添加一个在每个 for() 循环结束时句柄未被'[...]销毁的原因?*”,范围传统上由花括号形成。 (2认同)
  • 是的。但棘手的一点是,变量可见性的范围和变量存在的范围并不完全相同。后者由花式决定。前者有时会更紧一些(就像这里)。 (2认同)