【问题标题】:How can I avoid Perl::Critic warnings when I process a multi-line string with a filehandle?当我处理带有文件句柄的多行字符串时,如何避免 Perl::Critic 警告?
【发布时间】:2009-10-09 15:19:15
【问题描述】:

除了下面显示的 string-as-a-filehandle 解决方案之外,有没有人可以解决一次处理多行字符串的任务?

my $multiline_string = "line one\nline two\nline three\nline four";
my $filehandle;
open( $filehandle, '<', \$multiline_string )
    or croak("Can't open multi-line string as a filehandle: $!");
while ( defined (my $single_line = <$filehandle>) ) {
    # do some processing of $single_line here ...
}
close( $filehandle );

我不想使用文件句柄的理由很弱。当我在任何文件句柄上的打开命令和关闭命令之间有超过 10 条源代码行时,Test::Perl::Critic 会发牢骚。我正在对 $single_line 进行相当多的处理,因此在我的公开通话和关闭通话之间实际上有大约 40 行代码,我看不出有任何方法可以将其减少到 10 行。

而且我真的不想忽略构建中的 Perl::Critic 测试,因为这实际上是一个不错的测试,每当我在代码中打开一个实际的磁盘文件时,我都希望通过它。

【问题讨论】:

  • 如果$multiline_string 很大,split 返回的列表会更大,会破坏$multiline_string 的逐行处理。要么使用正则表达式一次匹配一行,要么将您所做的工作分解到子例程中。我个人更喜欢后者。
  • 哇。不去想子程序变通办法,我真是太傻了。有时我只是想不通。 :-)

标签: perl multiline filehandle perl-critic


【解决方案1】:

通过创建一个子例程并在文件的每一行调用它,让 Perl 评论家开心,让自己更开心。

use strict; use warnings;

sub do_something {
    my ($line) = @_;
    # do something with $line
}

open my $fh, '<', \$multiline_string
    or die "Cannot open scalar for reading: $!";

while(<$fh>) {
    chomp;
    do_something($_);
}

close $fh; 

【讨论】:

  • 这绝对是正确的做法。但是,您应该始终检查打开是否成功,并使用带有词法文件句柄的 3-arg 形式的 open。
  • 或者至少总是使用 3-arg open。如果您不想费心检查您的打开是否成功,您可以“使用 autodie”(以及严格和警告)。
【解决方案2】:

嗯,抱怨的目的不就是让你拥有只做一件事的更小的代码块吗?制作一个子程序来执行每一行所需的操作。

很多人建议split /\n/split /^/ 更像是文件句柄的方式。

【讨论】:

    【解决方案3】:

    怎么样:

    my $multiline_string = "line one\nline two\nline three\nline four";
    my @lines = split(/\n/,$multiline_string);
    foreach my $line (@lines) {
        #do stuff with string
    }
    

    【讨论】:

      【解决方案4】:

      我可能遗漏了一些东西,但你能做到吗:

      my @lines = split(/\n/,$multiline_string);
      foreach my $single_line (@lines) {
        ...
      }
      

      【讨论】:

      • 不要忘记您可以使用 /m 或 /s 选项处理带有正则表达式的多行字符串,如 perldoc perlre 中所述 - 这可能比在 \n 上拆分更容易,具体取决于您'正在寻找。
      【解决方案5】:

      早在我知道你可以将多行字符串塞入文件句柄之前,split:

      foreach my $single_line (split /\n/, $multiline_string) {
          # process $single_line here
          # although note that it doesn't end in a newline anymore
      }
      

      在此处插入关于使用文字和非便携式 \n 的免责声明。

      【讨论】:

        【解决方案6】:

        Perl::Critic 很好,但是当你开始沉迷于它的一些武断要求时,它开始浪费你的时间而不是节省它。我只是让文件句柄超出范围,不用担心关闭:

         my $multiline_string = "line one\nline two\nline three\nline four";
        
         {
             open my( $fh ), '<', \$multiline_string )
                 or croak("Can't open multi-line string as a filehandle: $!");
             while ( defined (my $single_line = <$fh>) ) {
                 # do some processing of $single_line here ...
             }
         }
        

        很多人使用正则表达式或拆分,但我认为这很草率。您无需在程序中创建新列表并占用更多内存。

        【讨论】:

          【解决方案7】:

          你可以使用正则表达式。

          #!/usr/bin/perl
          
          use strict;
          use warnings;
          
          my $s = "line one\nline two\nline three\nline four";
          
          while ($s =~ m'^(.*)$'gm) {
              print "'$1'\n";
          }
          
          die "Exited loop too early\n" unless pos $s == length $s;
          

          或者你可以使用split:

          for my $line ( split m'\n', $multiline_string ){
          
            # ...
          
          }
          

          【讨论】:

          • 正则表达式方法是最好的恕我直言。您不需要\G/m。使用:while ( $s =~ /(.+?)\n/g ) {split 很浪费,因为这意味着在内存中保留两个基本相同数据的副本。
          • *,不是 + 那里,否则你会跳过空行。和 ?没用。 \n 属于捕获更像是文件句柄读取方式。
          • 虽然 \G 可能不需要,但我会保留它;当您希望零碎地消耗所有字符串时,最好强制执行它(在循环后使用 m/\G.../gc 和 pos() 检查),这样您就不会意外写错正则表达式并丢失一些数据(就像你的 + 而不是 *)。
          • @ysth 请注意,OP 的字符串不以 \n 结尾。要正确处理该字符串,+ 是必需的,\n 必须是可选的。
          • @Sinan Ünür:那么你需要 /\G(?:.*\n|.+)/gc (或一些变体;有很多方法可以做到)。但如果真实数据末尾有换行符,我不会感到惊讶。
          【解决方案8】:

          我个人喜欢使用$/ 来分隔多行字符串中的行。

          my $multiline_string = "line one\nline two\nline three\nline four";
          foreach (split($/, $mutliline_string)) {
            process_file($_);
          }
          sub process_file {
            my $filename = shift;
            my $filehandle;
            open( $filehandle, '<', $filename )
                or croak("Can't open multi-line string as a filehandle: $!");
            while ( defined (my $single_line = <$filehandle>) ) {
                process_line($single_line);
            }
            close( $filehandle );
          }
          sub process_line {
            my $line = shift;
            ...
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-09
            • 1970-01-01
            • 1970-01-01
            • 2021-08-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多