Perl while循环仅输出最后一行问题及日志时间排序需求
日志读取与按时间合并的解决方案
咱们一步步来搞定你的问题:原代码只输出最后一行是因为每次循环都覆盖了数组,直接push拆分字段又会把所有内容揉成一维数组导致格式混乱。先解决完整输出日志的问题,再实现按时间合并的需求。
一、原代码问题分析
你写的while (my $line = <IN1>) { @fields1 = split " ", $line; }每次循环都会把@fields1重新赋值为当前行的拆分结果,循环结束后自然只保留最后一行内容。如果用push(@fields1 , split " ", $line),所有行的字段会被压进同一个一维数组,格式肯定混乱。
二、第一步:实现完整日志输出
要完整保存并输出日志,我们可以把每一行的字段存为二维数组(每行是一个独立的数组元素),这样就能保留所有行的结构。修改后的代码如下:
#!/usr/bin/perl use warnings; use strict; my $log1_file = "log1.log"; my $log2_file = "log2.log"; # 读取Log1到二维数组 open(my $in1, "<", $log1_file) or die "Could not open file $log1_file: $!"; my @log1; while (my $line = <$in1>) { chomp $line; next if $line =~ /^\s*$/; # 跳过空行 my @fields = split /\s+/, $line; push @log1, \@fields; } close $in1; # 读取Log2到二维数组 open(my $in2, "<", $log2_file) or die "Could not open file $log2_file: $!"; my @log2; while (my $line = <$in2>) { chomp $line; next if $line =~ /^\s*$/; my @fields = split /\s+/, $line; push @log2, \@fields; } close $in2; # 输出完整Log1 foreach my $row (@log1) { print join(" ", @$row) . "\n"; } print "\n"; # 空行分隔两个日志 # 输出完整Log2 foreach my $row (@log2) { print join(" ", @$row) . "\n"; }
运行这段代码就能得到你期望的完整日志输出,每行内容都会被正确保留并打印。
三、第二步:按Time字段合并排序输出
因为两个日志本身都是按Time递增排列的,我们用双指针法就能高效合并,完全基于标准Perl实现,不需要额外模块:
- 用两个指针分别指向两个日志的数据行(跳过表头,因为表头只需要输出一次)
- 循环比较当前指针指向行的Time值,输出Time较小的那一行,然后移动对应指针
- 当其中一个日志遍历完后,输出另一个日志剩下的所有行
代码实现如下:
#!/usr/bin/perl use warnings; use strict; my $log1_file = "log1.log"; my $log2_file = "log2.log"; # 读取Log1,保存表头和数据行 open(my $in1, "<", $log1_file) or die "Could not open file $log1_file: $!"; my @log1; while (my $line = <$in1>) { chomp $line; next if $line =~ /^\s*$/; my @fields = split /\s+/, $line; push @log1, \@fields; } close $in1; # 读取Log2,保存表头和数据行 open(my $in2, "<", $log2_file) or die "Could not open file $log2_file: $!"; my @log2; while (my $line = <$in2>) { chomp $line; next if $line =~ /^\s*$/; my @fields = split /\s+/, $line; push @log2, \@fields; } close $in2; # 先输出表头(两个日志表头一致,取Log1的即可) print join(" ", @{$log1[0]}) . "\n"; # 双指针合并数据行 my $i = 1; # Log1的数据行从索引1开始(索引0是表头) my $j = 1; # Log2的数据行从索引1开始 while ($i <= $#log1 && $j <= $#log2) { my $time1 = $log1[$i][0]; my $time2 = $log2[$j][0]; if ($time1 <= $time2) { print join(" ", @{$log1[$i]}) . "\n"; $i++; } else { print join(" ", @{$log2[$j]}) . "\n"; $j++; } } # 输出Log1剩余的行 while ($i <= $#log1) { print join(" ", @{$log1[$i]}) . "\n"; $i++; } # 输出Log2剩余的行 while ($j <= $#log2) { print join(" ", @{$log2[$j]}) . "\n"; $j++; }
运行后会得到按Time从小到大排序的合并日志,输出效果如下:
Time Src_id Des_id Address 0 34 56 x9870 1 35 57 x9871 2 36 58 x9872 3 37 59 x9873 4 38 60 x9874 5 39 61 x9875 6 40 62 x9876 7 41 63 x9877 8 42 64 x9878 9 43 65 x9879
补充小细节
- 用
open(my $in1, "<", $file)这种三参数写法比旧式的open(IN1, "<$file")更安全,能避免文件名里的特殊字符引发问题 chomp $line用来去掉每行末尾的换行符,防止输出多余空行- 跳过空行的处理是为了兼容日志里可能存在的空白行,让代码更健壮
内容的提问来源于stack exchange,提问作者Alexx
相关产品推荐
相关产品推荐

