【问题标题】:Perl in-place editing produces garbagePerl 就地编辑产生垃圾
【发布时间】:2015-06-24 22:06:27
【问题描述】:

我在就地文件编辑时遇到了问题,我浏览了几个小时没有结果。

我真的不想使用通用的临时文件方案,即将所有内容写入新文件并替换旧文件。我需要修改时间戳以反映实际更改、权限和所有权保持不变等。

如果我理解正确,使用 $I^ 只是临时文件方案的简写 - 还是我错了?

“+

到目前为止我的测试代码:

#!/usr/bin/perl
use strict;
use warnings;

open(FILE, "+<", "testingfile") or die "$!";

while (<FILE>) {
    print;
    s/world/WORLD/;
    print FILE $_;
    print;
}

“testingfile”有三行,我现在只想将“world”替换为“WORLD”:

hello
world
foo

结果

当我运行 Perl 脚本时,会产生垃圾并且终端一直挂起直到被中断 (Ctrl+C):

hello
hello
foo
foo
o
o
llo
llo
ÈÈ'>jËNgs}>¾ØKeh%P8*   *        +       +      p+      ÑÑÀ+    +       p+      p+      ¨° #!/u8in/puse ct;
ÈÈ'>jËNgs}>¾ØKeh%P8*   *        +       +      p+      ÑÑÀ+    +       p+      p+      ¨° #!/u8in/puse ct;

“测试文件”现在包含:

hello
world
foo
hello
hello
foo

我在 SunOS (Solaris) 生产系统上运行旧的 Perl:

This is perl, v5.8.4 built for i86pc-solaris-64int

【问题讨论】:

  • 您将在现实中做出什么样的改变?将world 更改为WORLD 很简单,因为两个字符串的长度相同。如果您想将数据附加到文件末尾,那么这很简单,但如果您想缩短或 - 更糟糕的是 - 延长文件末尾之前的行,那么它变得更加困难

标签: perl in-place


【解决方案1】:

最直接的方法是使用Tie::File,它允许您通过简单地修改数组来编辑文本文件。它确实有慢的名声,但是只有自己尝试一下才能知道它是否

您的示例代码将变成这样

#!/usr/bin/perl
use strict;
use warnings;

use Tie::File;

tie my @file, 'Tie::File', 'testingfile' or die $!;

s/world/WORLD/ for @file;

untie @file;

【讨论】:

    【解决方案2】:

    您需要了解seek 命令才能在文件中移动。您的文件句柄 FILE 有一个光标。从FILE 读取后,它的光标指向您刚刚读取的数据的末尾。然后你在FILE上写,你并没有覆盖你刚刚读取的数据,而是你即将读取的数据。

    这是您的文件。首次打开时,光标位于文件开头。

     h e l l o \n w o r l d \n f o o \n EOF
    ^
    

    接下来,您使用&lt;FILE&gt; 操作读取一行输入。这会将文本“hello\n”加载到变量$_ 中并移动FILE 的光标:

     h e l l o \n w o r l d \n f o o \n EOF
                 ^
    

    接下来,您的替换失败并且不会更改$_,并且您将$_ 的内容打印到FILE。写作从光标处开始,你得到

     h e l l o \n h e l l o \n f o o \n EOF
                              ^
    

    下次读取时,在$_中得到foo\n,将光标移动到文件末尾,然后在文件末尾重写$_

     h e l l o \n h e l l o \n f o o \n f o o \n EOF
                                                ^
    

    使用seek 命令移动光标。也许像

    open(FILE, "+<", "testingfile") or die "$!";
    
    while (<FILE>) {
        print;
        if (s/world/WORLD/) {
            seek FILE, -length($_), 1;   # move back by length of $_
            print FILE $_;
        }
        print;
    }
    

    正如@Borodin 指出的那样,如果您想在浏览文件时延长或缩短$_,这会变得更加复杂。

    【讨论】:

    • 感谢您的详尽解释。我仍然想知道为什么我得到垃圾输出和终端挂起;我想我一定触发了一些错误。我最终使用了一种更“简单”的方法 - 将文件读入内存,执行更改并在进行更改时重新打开文件进行写入。听起来没有简单的方法来插入或删除字节,只能覆盖现有的?
    【解决方案3】:
    #!/usr/bin/perl
    use strict;
    use warnings;
    
    # getlines
    open(FILE, "<", "testingfile") or die "$!";
    my @lines = <FILE>;
    my $line = "";
    close(FILE);
    
    # open file again but this time for writing
    open(FILE, ">", "testingfile") or die "$!";
    
    # write adjusted text
    foreach $line (@lines) {
        $line =~s/world/WORLD/;
        print FILE "$line";
        print "$line";
    }
    

    【讨论】:

    • 我实际上最终做了一个变体: 第 1 步:以读取模式打开文件并存储到 @lines 第 2 步:遍历行,在适用时修改内容,并通过增加记录任何更改一个柜台。第 3 步:关闭文件 第 4 步:如果 $changes 计数器不为零,则以写入模式重新打开文件并刷新内容。
    【解决方案4】:

    就地编辑不会做你正在做的事情。它重命名原始文件,然后使用原始名称打开一个新文件。它从重命名的文件中读取并写入原始文件名。 -I的解释见perlrun

    【讨论】:

      猜你喜欢
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      相关资源
      最近更新 更多