逐行读取文件并替换字符串,然后打印出文件

时间:2018-08-23 07:31:24

标签: regex bash perl regex-group

当前,我正在使用以下代码来查找替换字符串的字符串,并将其打印到输出文件中,其名称与输入文件中的名称相同,但位于替换的文件夹中

use Tie::File;
@files = <*>;
foreach $file (@files) {
    my $filename = $file;
    open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!";
    open(NEWFILE,"> ./replaced/$filename");

   while(my $variable=<$fh>){

      s/Insertstoredprocedure ( / Insertstoredprocedure('$filename',/g;
      s/SuccessSp()/SuccessSp()('$filename')/g;

  print NEWFILE "$variable";
  print "done\n";
  }
}

此脚本旨在替换所有内容,并将更改后的文件放入替换文件夹...。此操作不起作用,这会产生错误...我如何替换和打印其中的所有文件当前目录..

2 个答案:

答案 0 :(得分:1)

直接错误和危险信号:

  • 一旦您分配了while ($variable = <$fh>)$_就不会设置为<$fh>所读取的内容;保留原样(此处未定义);因此,与之匹配的正则表达式(默认情况下)将无法运行

  • 要转义的正则表达式中需要将原义字符匹配的括号

  • 该代码处理当前目录<*>中的所有文件-在此代码中,该文件本身也可能包含脚本本身,并且没有保护或检查内容

我假设使用./replaced/是指replaced/在脚本所在的目录中,而不是在当前工作目录中(如pwd);这些通常是不一样的。请澄清。

已更正,但有其他更改

use warnings;
use strict;
use feature qw(say);

use FindBin qw($RealBin);

use open ':std', ':encoding(UTF-8)';

my @files = grep { -f } @ARGV;    # add further checks of user input

my $outdir = "$RealBin/replaced";
mkdir $outdir if not -d $outdir;  # or use File::Path

foreach my $file (@files) {
    my $fout = "$outdir/$file";
    open my $fh, '<', $file or die "Can't open $file: $!";
    open my $fh_out, '>', $fout or die "Can't open $fout: $!";

    while (my $line = <$fh>) {
        $line =~ s/Insertstoredprocedure \( / Insertstoredprocedure('$file',/g;
        $line =~ s/SuccessSp\(\)/SuccessSp()('$file')/g;

        print $fh_out $line;
    }
    say "done, $file --> $fout";
}

对问题代码的评论

  • 始终使用use warnings;use strict;

  • 来启动程序
  • <*>从当前目录读取所有条目,这带来了一些棘手的问题;其中之一可能包括脚本本身。更重要的是,通过这种方式,可以将脚本与要处理的数据固定在一起。为什么不接受用户输入呢?我将其更改为使用在命令行上提交的文件名(大概是文件名)。然后在Linux上,您可以将脚本调用为

    script.pl *.ext
    

    如果必须,您仍然可以使用script.pl *,但随后需要进行更多检查,尤其是确保跳过脚本本身(如果从其目录运行)。例如参见this post

  • 始终检查输入是否适当。在这种情况下,您至少可以确保仅处理纯文件。我只是使用-f filetest operator进行过滤,但另一个选择是将输入内容提交后再进行检查,以便您可以告知用户输入不足的情况

  • 我认为不需要引入$filename;只需使用topicalizer $file

  • 如果您使用UTF8,最好使用open pragma;然后所有文件和流都得到照顾

  • 对所有内容都使用词汇文件句柄,因此也要编写文件

  • 从文件中读取一行时,为什么不将其命名为$line?代码中的“ $variable”是如此通用,以至于无法提供关于该变量是什么的任何线索

  • 一旦您在while条件下分配了一个命名变量,那么$_不会设置为读取的内容;只有while (<$fh>)会发生这种情况。在这段代码中,它在循环体内是未定义的。因此,在正则表达式中,您需要使用 that 变量,将 行分配给该变量(而不是将其保留为默认$_

  • 如果要将正则表达式中具有特殊含义的字符作为文字字符进行匹配,则必须转义这些字符,并且括号就是其中之一。有多种方法可以实现,我使用您的文本并直接使用\进行转义(无需在替换部分进行转义)

  • 原则上,使用qr operator将模式定义为单独的变量是一个好主意。然后,您可以使用quotemeta

  • 来转义所有特殊字符。

我无法知道您的(已更正的)正则表达式是否达到了预期的目的,因此我只能解决明显的错误。请显示数据样本和所需的输出。

答案 1 :(得分:0)

请尝试以下方法吗?我假设在当前工作目录中找到了“已替换”。

use strict;
use warnings;
use Tie::File;
use English qw(-no_match_vars);

my @files = grep {-f} <*>;

-d './replaced/' or mkdir './replaced/';

foreach my $file (@files) {
    open my $fh, '<:encoding(UTF-8)', $file
      or die "Could not open file '$file': $OS_ERROR";
    open my $newfh, '>', "./replaced/$file"
      or die "Could not create new file './replaced/$file': $OS_ERROR";
    while (<$fh>) {
        s/Insertstoredprocedure\s*\(/Insertstoredprocedure('$file'/g;
        s/SuccessSp\s*\(/SuccessSp('$file'/g;
        print {$newfh} $_;
    }
    close $fh or die $OS_ERROR;
    close $newfh or die $OS_ERROR;
    print 'DONE with file: '.$file."\n";
}

强制性更改:

  1. 过滤器(grep)<*>,以便我们丢弃目录。如果没有,尝试打开目录时会出现权限错误
  2. 将正则表达式的括号(匹配)括起来\(
  3. 修复代码中的错误,该错误使您逐行匹配文件,但替换了$_变量而不是$variable。现在,它始终可以与$_
  4. 一起使用
  5. 修复了最后一个正则表达式,它带有一些不需要的括号
  6. 您必须打印“完成!”在while循环之外,因为while是用于每一行的。

建议的更改:

  1. 添加了use strictuse warnings非常推荐
  2. 使用英语,将$!用作$OS_ERROR
  3. my添加到foreach的变量
  4. 读取/写入后关闭文件。
  5. 创建“已替换”文件夹(如果该文件夹不存在)
  6. 对于输出文件,请使用词汇文件句柄,并打开open my $newfh, '>', ...的3个参数