【问题标题】:How do I read a file line by line while modifying lines as needed?如何在根据需要修改行的同时逐行读取文件?
【发布时间】:2011-01-19 07:53:32
【问题描述】:

在 Perl 中,我试图逐行读取文件并处理每一行,并根据需要对其进行修改。 到目前为止,我正在阅读能够做到这一点的唯一方法是将文件读入数组,根据需要修改数组的每个元素,然后在完成后将其输出回文件。

有没有更好的方法来做到这一点,也许我可以通过某种方式替换单行?

现在,我的处理代码如下所示:

while (my $line = <FILE>)
{
    # process line here
    # ...........

    print FILE $line;
}

我的代码似乎非常接近,除了它在我当前所在的行之后替换了一行,所以似乎如果我可以将文件指针向上备份一行,它将写入正确的位置文件。

我在正确的轨道上吗?我需要从这里做什么来备份文件指针,以便它写入我正在读取的同一行?


编辑:

在我收到的答案中,使用local $^ITie::File 效果很好。 我最终选择了Tie::File,这样我就不必打印出文件的每一行。这样,如果脚本中途发生了什么事,我的文件就不会被弄乱。

我的新代码如下所示:

use Tie::File;

chomp(my $filename = $ARGV[0]);
tie my @array, 'Tie::File', $filename or die $!;

foreach my $line(@array)
{
    # ...... line processing happens here .......
    # ...... $line is automatically written to file if $line is changed .......
}

【问题讨论】:

    标签: perl


    【解决方案1】:

    我需要从这里做什么来备份文件指针,以便它写入我正在读取的同一行?

    这无济于事,除非您打算编写的每一行都与您要替换的行长度相同(在这种情况下,您要查找的工具是 seektell)。不过,对于普通的编辑,标准文件模型并没有将其剪切为就地替换位。

    幸运的是,Perl 提供了一项功能,可以让您轻松完成所需的工作,称为“就地编辑模式”,在这种模式下,源文件要么被重命名,要么被取消链接,并且输出定向到具有相同名称的新文件。大多数情况下,它通过启用-i command-line switch 以及-p-n 开关进行逐行编辑来使用,但您也可以在程序中使用$^I special variable 启用它。

    示例代码:

    {   # Create a scope to localize variables in.
        # If you want to back up the originals, set $^I to ".bak" instead.
        local $^I = "";
        # Set @ARGV to the file you want to process, or a list of files.
        local @ARGV = ("file.txt");
    
        while (my $line = <>) {
            # Process $line here.
            print $line;
        }
    }
    

    【讨论】:

      【解决方案2】:

      我不认为像你一样从文件中读取并同时写入是一个好主意。

      您可以使用Tie::File。它将文件的行与数组联系起来。您可以根据需要修改数组,从而在后台透明地修改文件。

      【讨论】:

      • 这正是我想要的。谢谢!
      【解决方案3】:

      可能与问题无关,但可以在命令行上快速就地更改文件,例如

      # convert MS line endings to UNIX:
      perl -p -i -e 's{\r\n}{\n}' my_file.txt
      

      代码中的行是 $_(-e 的参数)并且该行被打印出来,所以它是这样的就地版本:

      perl -e '$line = $_; $line =~ s{\r\n}{\n}; print $line' < windows.txt > unix.txt  
      

      【讨论】:

        【解决方案4】:

        我倾向于按照this answer 中的假设进行操作(参见sub precommit_hook):

        首先,将整个文件读入一个数组:

        open my $handle,'<:utf8',$name 
            or croak "Error reading file contents of $name\n";
        my @content = <$handle>;
        close $handle or croak "unable to close";
        

        然后,处理数组的每一行并将其写入文件:

        # now, write it, ignoring the comment lines
        open my $handle, '>:utf8', $name
        or croak "Opening $name for writing failed\n";
        flock $handle, LOCK_EX;
        
        foreach my $line(@content){
          # TODO: modify the line here
          print {$handle} $line . "\n";
        }
        
        close $handle or croak "unable to close";
        

        这样做的缺点是整个文件会被重写,如果你退出太早(例如在调试期间),文件就会被弄乱。

        【讨论】:

        • 如果您要做的只是关闭文件,则永远不要使用flock $fh, LOCK_UN 解锁文件。 close 将在关闭文件时解锁文件,但首先它会刷新尚未写入的所有数据,从而防止竞争。
        • @hobbs:这就是我喜欢这样的原因:你回答了一些问题,然后更有能力的人出现并告诉你你这样做的方式不对。谢谢你教我!
        猜你喜欢
        • 2017-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-14
        • 1970-01-01
        • 2011-11-02
        • 2012-07-02
        • 2018-07-31
        相关资源
        最近更新 更多