【问题标题】:Perl script to search pattern and concat lines in a file用于搜索文件中的模式和连续行的 Perl 脚本
【发布时间】:2010-11-02 14:44:07
【问题描述】:

我有一个文本文件(基本上是带有日期、时间戳和一些数据的错误日志),格式如下:

mm/dd/yy 12:00:00:0001  
This is line 1
This is line 2

mm/dd/yy 12:00:00:0004  
This is line 3
This is line 4
This is line 5


mm/dd/yy 12:00:00:0004
This is line 6
This is line 7

我是 Perl 的新手,需要编写一个脚本来搜索文件中的时间戳并合并其中具有相同时间戳的数据。

我期待上述示例的以下输出。

mm/dd/yy 12:00:00:0001  
This is line 1
This is line 2

mm/dd/yy 12:00:00:0004  
This is line 3
This is line 4
This is line 5
This is line 6
This is line 7

完成这项工作的最佳方法是什么?

【问题讨论】:

  • 来自非 Perlish 人:在我看来,您可以遍历每一行,并测试它是否与您的时间戳正则表达式匹配。如果是这样,你保存它;如果它与上次保存的时间戳相同,则将其删除。
  • 是否会对时间戳进行排序,或者换句话说,时间戳A是否会在不同的时间戳B之后再次出现?
  • 如果是错误日志,我假设时间戳是有序的。除非你的错误是时空连续体中的一个洞。
  • @mmyers:不要假设太多。大文件可能是来自不同服务器的日志文件的串联。在每个部分中,时间戳都是排序的,但不是跨文件排序的。这只是我以前见过的一个案例。 ;)
  • 啊哈。还好我当时没有写答案。

标签: regex perl string text


【解决方案1】:

我之前不得不对一些非常大的文件执行此任务,并且时间戳没有按顺序排列。我不想把它全部存储在内存中。我通过使用三遍解决方案完成了任务:

  • 用时间戳标记每个输入行并保存在临时文件中
  • 使用快速排序器对临时文件进行排序,例如 sort(1)
  • 将排序后的文件恢复为起始格式

这对于我的任务来说已经足够快了,我可以让它在我去喝杯咖啡的时候运行,但如果你真的需要很快得到结果,你可能需要做一些更花哨的事情。

使用严格; 使用警告; 使用 File::Temp qw(tempfile); 我的($temp_fh,$temp_filename)= tempfile(UNLINK => 1); # 读取每一行,用时间戳标记,并写入临时文件 # 稍后将排序和撤消。 我的 $current_timestamp = ''; 线:同时() { 咀嚼; if( m|^\d\d/\d\d/\d\d \d\d​​:\d\d:\d\d:\d\d\d\d$| ) # 时间戳行 { $current_timestamp = $_; 下一行; } elsif( m|\S| ) # 非空白行(不是“空白行”) { 打印 $temp_fh "[$current_timestamp] $_\n"; } else #空行 { 下一行; } } 关闭 $temp_fh; # 使用一些非常快速的排序器按行对文件进行排序 系统(“排序”,qw(-o sorted.txt),$temp_filename); # 读取排序后的文件并转回起始格式 open my($in), " ) { 我的( $timestamp, $line ) = m/\[(.*?)] (.*)/; if( $timestamp 和 $current_timestamp ) { $current_timestamp = $timestamp; 打印 $/, $timestamp, $/; } 打印 $line, $/; } 取消链接 $temp_file, 'sorted.txt'; __结尾__ 01/01/70 12:00:00:0004 这是第 3 行 这是第 4 行 这是第 5 行 01/01/70 12:00:00:0001 这是第 1 行 这是第 2 行 01/01/70 12:00:00:0004 这是第 6 行 这是第 7 行

【讨论】:

  • 你不需要保存'sorted.txt'文件,你可以引入安全问题。您可以使用 open '-|'表单重定向排序输出。
  • 获得中间结果有时很好(特别是如果需要很长时间才能完成),但这没什么大不了的。我不确定您认为存在什么安全问题,但还有其他需要担心的事情。
  • 太棒了。该脚本有效。我不得不稍微调整时间戳的正则表达式以匹配我的,但让它工作。非常感谢!
【解决方案2】:

如果日志文件不是太大而无法保存在内存中,您可以只保留日期字符串 => 文本的哈希值。像这样的:

my %h;
my $cur = "*** No date ***";
while(<>) {
  if (m"^(\d\d/\d\d/\d\d \d\d:\d\d:\d\d:\d{4})") {
    $cur = $1;
  } else {
    $h{$cur} .= $_ unless /^\s*$/;
  }
}

print "$_\n$h{$_}\n" foreach (sort keys %h);

你把它保存为 t.pl 并以 perl t.pl

【讨论】:

  • 这对我来说牙签太多了:m{^( \d{2} / \d{2} / \d{2} \s \d{2} : \d{2 } : \d{2} : \d{4} ) }x
  • 思南,\d\d 比 \d{2} 短,对我来说更好看。至于更改为 m 以避免另一个反斜杠 - 这是有道理的。谢谢,编辑我的答案。
【解决方案3】:

如果输入量很大,分两个阶段执行此操作可能是个好主意:创建一个带有单个表的 SQLite 数据库,其中一个表包含时间戳和行列(可能还有行号和文件名)。然后你可以以任何你想要的方式输出数据。

【讨论】:

    【解决方案4】:

    考虑这个解决方案...

        #!/usr/bin/perl
    
        use strict;
    
        my (%time, $id);
        while (<DATA>) {
            if ( /^mm/ ... /\n\n/ ) {
                chomp;
                s/^mm\/dd\/yy\s(.*)// and $id = $1;
                next if ( /^mm/ || /^$/ );
                push (@{$time{$id}}, $_);
           }
    
    }
    
    for my $i ( keys %time ) {
        print "mm/dd/yy $i\n";
        for my $j ( @{$time{$i}} ) {
            print "$j\n";
        }
        print "\n";
    }
    
    __DATA__
    mm/dd/yy 12:00:00:0001
    This is line 1
    This is line 2
    
    mm/dd/yy 12:00:00:0004
    This is line 3
    This is line 4
    This is line 5
    
    
    mm/dd/yy 12:00:00:0004
    This is line 6
    This is line 7
    

    【讨论】:

    • 谢谢...但这似乎没有将第 3-5 行与第 6 行和第 7 行结合起来。
    猜你喜欢
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    • 2017-08-20
    • 2011-12-07
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多