我的 perl 替换破折号模式清除了所有文件内容

Posted

技术标签:

【中文标题】我的 perl 替换破折号模式清除了所有文件内容【英文标题】:My perl replace dash pattern wipes out all file contents 【发布时间】:2022-01-16 17:48:12 【问题描述】:

在下面的代码中,我试图用相应数量的空格替换“---- ------”模式。当我运行代码时,所有文件内容都被清除了。我不知道为什么。我在下面有要进行替换的示例文本。 我将不胜感激。

#!/usr/bin/perl -w
use strict;
use warnings;
use File::Path;
use File::Copy;
use feature 'unicode_strings';
use utf8;    

my $IN;
my $dir;

opendir $dir, $Dirs[$mm] or die "opendir failed on $Dirs[$mm]: $! ($^E)";

while (my $file = readdir $dir)         #reading input directory
    next if -d $file;

    if ($file =~ /Ranked/) 
        print "$file\n";
        open ($IN, "+> $Dirs[$mm]/$file") or die "open '$file': failed $! ($^E)";

        while (<$IN>) 
            s/---- ------/           /g;
            print "$_\n";
        

        close $dir;
        exit;
    


END
                          Ranked Rainfall: Jun
                          ********************
CHIPAT01     CHIPEP01     CHOMA001     ISOKA001     KABOMP01     KABWE001     KABWE002     KAFIRO01     KAFUE001     KALABO01     KAOMA001     KASAMA01     KASEMP01

Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl 1968 17.0 2003 13.2 2000 29.2 2004 3.1 1988 13.3 1988 2.8 ---- ------ 1967 2.8 1966 1.3 2009 2.3 1965 10.7 1968 11.9 1988 6.0 1983 5.8 2011 6.1 1966 14.0 2002 2.0 1965 9.4 1960 0.5 ---- ------ ---- ------ 1960 0.6 ---- ------ 2009 1.8 1952 0.8 1940 5.1 1974 4.6 1996 0.6 1997 4.5 ---- ------ 1966 2.3 1986 0.1 ---- ------ ---- ------ ---- ------ ---- ------ 1966 0.3 1936 0.3 1966 4.8 1960 3.3 2007 0.5 1988 4.0 ---- ------ Ave 0.5 Ave 0.1 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.2 1965 1.8 1999 0.3 2002 3.7 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1948 1.6 Ave 0.1 2003 3.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1971 1.0 ---- ------ 1955 3.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1978 0.9 ---- ------ 1976 2.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1961 0.5 ---- ------ 1975 1.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1946 0.5 ---- ------ 1978 1.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1967 0.5 ---- ------ 2011 1.4 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.4 ---- ------ 1960 1.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1964 0.3 ---- ------ 2009 1.0 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1996 0.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1969 0.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1958 0.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1979 0.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------

【问题讨论】:

请阅读open 的文档,尤其是有关模式的文档。也许你打算open(my $in, '&lt;', $fname) or die "Error message"。以下 [webpage] 具有打开模式表,其中包含模式描述。以"+&gt; $fname" 模式打开的文件会截断文件,然后允许写入它——你截断文件,然后才尝试写入它。 链接到webpage。 可以使用类似perl -pi.bak -e 's/-/ /g' some_file.dat的命令对文件进行所需的操作 代码在当前条件下无法运行:Global symbol "@Dirs" requires explicit package name$mm 也是如此。 【参考方案1】:

同时读取和写入同一个文件需要小心。在这里,您正在使用+&gt; 同时读取和写入$file。但是,open 的文档警告说:

您可以在&gt;&lt; 前面放置一个+,表示您希望对文件具有读写权限;因此+&lt; 几乎总是首选用于读/写更新——+&gt; 模式会首先破坏文件

相反,您应该写入一个临时文件。完成写入后,您可以将原始文件替换为临时文件。要打开一个临时文件,您可以使用File::Temp 模块。要将原始文件替换为新文件,您可以使用File::Copy 模块中的move。您的代码已更正:

use File::Temp qw(tempfile);
use File::Copy;

while (my $file=readdir $dir) 
    next if -d "$dir/$file";
    if($file =~ /Ranked/)
        print "$file\n";

        my ($out_fh, $out_filename) = tempfile();
        open (my $IN, "<", "$Dirs[$mm]/$file") or die "open '$file': failed $! ($^E)";
        while( <$IN> )
            s/---- ------/           /g;
            print $out_fh $_;
        
        close $out_fh;
        copy($out_filename, "$Dirs[$mm]/$file");
    

请注意,在 copy 之前关闭 $out_fh 很重要。


小提示:

next if -d $file 应该是next if -d "$dir/$file"

您应该在尽可能窄的范围内声明您的变量。这意味着您应该使用open my $IN, ...,而不是使用my $IN; 开始您的脚本,然后再使用open $IN, ...my $diropendir 也一样。


替代解决方案:

Perl 具有用于就地文件编辑的内置机制,但它更适合单行而不是完整脚本。这是由-i command-line switch 启用的,但如果需要也可以使用outside of one-liners。

一些 CPAN 包(例如 File::Inplace 可用于避免自己编写整个 tempfile/move 样板。

【讨论】:

这很完美@Dada。非常感谢。只剩下一个问题了。之后留下的空白行,我想摆脱它们。我的代码中有 s/^\s+$//g,但它似乎不起作用。 该代码 (s/^\s+$//g) 确实有效。再次感谢。 @ZiloreMumba 完美。乐于助人:)

以上是关于我的 perl 替换破折号模式清除了所有文件内容的主要内容,如果未能解决你的问题,请参考以下文章

用空格替换文本区域中的逗号、破折号和输入键

PERL:用破折号读取社会保障号的正则表达式

perl 遍历指定目录下的所有文件,替换指定文本内容,返回受影响的文件路径

批量删除目录中所有文件中的空白,并替换为破折号“-”;

Perl处理数据:s替换split和Join

替换perl中一行中第n次出现后的所有出现