【问题标题】:Perl - How to Read, Filter & Output resultsPerl - 如何读取、过滤和输出结果
【发布时间】:2014-05-14 10:33:51
【问题描述】:

场景:我是一名初级 C# 开发人员,但最近(3 天)开始学习 Perl 的批处理文件。我需要解析一个文本文件,提取一些关键数据,然后将关键数据输出到一个新的文本文件。似乎总是如此,网上有很多关于如何“读取”文件、“写入”到文件、“逐行存储”到数组、“过滤”这个和那,yadda yadda,但没有讨论读取,过滤,写入的整个过程。尝试将网络上的示例拼接在一起是不好的,因为似乎没有一个可以作为连贯的代码一起工作。来自 C#,Perl 的语法结构令人困惑。我只是需要一些关于这个过程的建议。

我的目标是解析一个文本文件,按日期挑出与下面类似的所有行,并仅将第二个数字组的前 8 位和第三个数字组的 5 位输出到新的文本文件.

11122 20100223454345 ....random text..... [keyword that identifies all the 
entries I need]... random text 0.0034543345 

我知道正则表达式可能是最好的选择,并且大部分表达式都已编写,但它在 Perl 中不起作用!

问题:有人可以展示一个简单的(虚拟)示例,说明如何读取、过滤(使用虚拟正则表达式)文件,然后将(虚拟)结果输出到新文件吗?我不关心功能细节,我可以学习那些,我只需要 Perl 使用的语法结构。例如:

 open(FH, '<', 'dummy1.txt')
 open(NFH, '>', 'dummy2.txt')

 @array; or $dumb;
 while(<FH>) 
 {
    filter each line [REGEX] and shove it into [@array or $dumb scalar]
 } 
 print(join(',', @array)) to dummy2.txt
 close FH;
 close NFH;

注意:由于各种原因,我无法在此处粘贴我的源代码,抱歉。任何帮助表示赞赏。

更新:答案:

非常感谢所有对我的问题提供见解的人。在阅读了您的回复并进行了进一步的研究后,我了解到在 Perl 中有许多方法可以完成相同的任务(我不喜欢)。最后,这就是我解决问题的方法,而 IMO 对于那些有类似问题的人来说,这是最干净、最简洁的解决方案。再次感谢所有帮助。

      #======================================================================
  # 1. READ FILE:   inputFile.txt
  # 2. CREATE FILE: outputFile.txt
  # 3. WRITE TO:    outputFile.txt IF line matches REGEX constraints
  # 4. CLOSE FILES: outputFile.txt & inputFile.txt
  #==========================================================================

  #1
  $readFile = 'C:/.../.../inputFile.txt';
  open(FH, '<', $readFile) or Error("Could not read file ($!)");

  #2
  $writeFile = 'C:/.../.../outputFile.txt';
  open(NFH, '>', $writeFile) or Error("Cannot write to file ($!)");

  #3
  @lines = <FH>;
  LINE: foreach $line (@lines)
  {
     if ($line =~ m/(201403\d\d).*KEYWORD.*time was (\d+\.\d+)/)
     {
        $date = $1;
        $elapsedtime = $2;
        print NFH "$date,$elapsedtime\n";
     }
  }

  #4
  close NFH;
  close FH;

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    perlfaq5 - How do I change, delete, or insert a line in a file, or append to the beginning of a file? 涵盖了如何使用文件的大部分不同场景。

    不过,我还要补充一点,始终以 use strict;use warnings; 开始您的脚本,并且因为您正在处理文件,use autodie; 也会为您服务。

    考虑到这一点,快速存根将如下所示:

    use strict;
    use warnings;
    use autodie;
    
    open my $infh, '<', 'dummy1.txt';
    open my $outfh, '>', 'dummy2.txt';
    
    while (my $line = <$infh>) {
        chomp $line; # Remove \n
    
        if (Whatever magically processing here) {
            print $outfh, "your new data";
        }
    }
    

    【讨论】:

    • 当每个人的答案都正确时,总是很难选择正确的答案。最后,您提供了一些极好的智慧珍珠(没有双关语)。
    【解决方案2】:
    while(<FH>)
    {
      # variable $_ contains the current line
    
      if(m/regex_goes_here/) #by default, the regex match operator m// attempts to match the default $_ variable  
      {  
        #do actions  
      }  
    }  
    

    另请注意,m/regex//regex/ 相同

    参考:

    对于从正则表达式匹配中捕获变量,THIS 可能会有所帮助

    编辑

    如果您想要一个不同于默认$_ 的变量,正如@Miller 建议的那样,请使用while($line = &lt;FH&gt;),后跟if($line =~ m/regex_goes_here/)

    =~Binding Operator

    【讨论】:

    • 我不明白 $_ 是什么。我读过它,但最好我能猜出它是某种神秘的仙女变量,它突然出现并用某些东西做某事,并且再也不会被看到,一切都来自窗帘后面。理解 $_ 是让它发挥作用的关键吗?抱歉,就像我说的,我是 C# 人。
    • 检查我的答案中的 perldoc 通用变量链接。几个函数使用$_ 作为默认占位符变量(在循环文件、数组等时)
    • 还编辑了如何避免使用$_ 的答案。理解$_ 不是关键,但有可能提高代码的可读性(以及易于编写)
    【解决方案3】:
    use strict;
    use warnings;
    use autodie;
    use feature qw(say);
    
    use constant {
        INPUT_FILE  => "NAME_OF_INPUT_FILE",
        OUTPUT_FILE => "NAME_OF_OUTPUT_FILE",
        FILTER      => qr/regex_for_line_to_filter/,
    };
    
    open my $in_fh, "<", INPUT_FILE;
    open my $out_fh, ">", OUTPUT_FILE;
    
    while ( my $line = <$in_fh> ) {
        chomp $line;
        next unless $line =~ FILTER;
        $line =~ s/regular_expression/replacement/;
        say {$out_fh} $line;
    }
    close $in_file;
    close $out_file;
    

    $in_file 是您的输入文件,$out_fh 是您的输出文件。我基本上都打开了,然后循环输入。 chomp 从末尾删除 \n。我总是建议这样做。

    next 进入循环的下一次迭代,除非我匹配 FILTER,这是一个匹配您想要保留的行的正则表达式。这等同于:

    if ( $line !~ FILTER ) {
        next;
    }
    

    然后我使用 substitution 命令来获取我想要的行的部分,并将它们放入我想要的输出中。我可能会更好地扩展这一点。也许使用split 将我的线路分成不同的部分,只使用我想要的部分。然后我可以使用substr 从选定的片段中提取子字符串。

    say 命令类似于print,只是它在末尾自动添加了一个 NL。这就是您将一行写入文件的方式。

    现在,获取Learning Perl 并阅读它。如果你知道任何编程。读完本书的前半部分应该不会超过一个星期。这应该足以编写这样的程序。像引用和面向对象这样更复杂的东西可能需要更长的时间。

    可以在http://perldoc.perl.org 找到在线文档。您可以在那里查找称为 pragmasuse 语句。还提供有关个人 functions 的文档。

    【讨论】:

      【解决方案4】:

      一个提示。不要显式打开输入和输出文件的文件句柄。而是从 STDIN 读取并写入 STDOUT。您的程序将更加灵活且易于使用,因为您可以将其视为 Unix 过滤器。

      $ your_filter_program < your_input.txt > your_output.txt
      

      这样做实际上也使您的程序更易于编写。

      while (<>) { # <> reads from STDIN
        # transform your data (which is in $_) in some way
        ...
        print; # prints $_ to STDOUT
      }
      

      您可能会发现Data Munging with Perl 的前几章很有用。

      【讨论】:

        【解决方案5】:

        如果我理解得很好,这个班轮就可以完成这项工作:

        perl -ane 'print substr($F[1],0,8),"\t",substr($F[-1],0,5),"\n" if /keyword/' in.txt
        

        假设 in.txt 是:

        11122 20100223454345 ....random text..... [keyword that identifies all the entries I need]... random text 0.0034543345
        11122 30100223454345 ....random text..... [ that identifies all the entries I need]... random text 0.124543345
        11122 40100223454345 ....random text..... [keyword that identifies all the entries I need]... random text 0.65487
        11122 50100223454345 ....random text..... [ that identifies all the entries I need]... random text 0.6215
        

        输出:

        20100223    0.003
        40100223    0.654
        

        【讨论】:

          猜你喜欢
          • 2020-08-13
          • 1970-01-01
          • 1970-01-01
          • 2021-04-24
          • 1970-01-01
          • 2021-08-08
          • 2021-03-27
          • 1970-01-01
          • 2023-03-29
          相关资源
          最近更新 更多