如何修改Perl代码,实现两个目录文件名的子串匹配与不匹配项输出?
解决你的Perl目录文件匹配问题
我看了你的代码,现在它会输出所有行,主要是几个小问题导致的,我来帮你修复并优化一下:
原代码的核心问题
- 数组索引越界:你在内层循环用了
$j <= @b和$j <= @a,但Perl数组的索引是从0到数组长度减1的,当$j等于数组长度时,访问的是超出数组范围的空元素,这会让子串匹配始终失败,$flag一直是0,所以所有文件都被当成不匹配的输出了。 - 未处理换行符:用
ls获取的文件名末尾都带着换行符,直接做substr会把换行符也算进字符串里,导致截取的子串不对,自然匹配不上。 - 嵌套循环效率低:两层循环遍历数组不仅慢,还容易出错,换成哈希表存储子串的话,检查匹配会高效很多。
优化后的代码(推荐用这个)
这个版本用哈希表来存储子串,逻辑更清晰,效率也更高:
use strict; use warnings; my $sdipath = "/home/ec2-user/TRF_DATA/FinancialStatement/FINALSDI"; my $sparkpath = "/home/ec2-user/TRF_DATA/FinancialStatement/FINALSPARK"; # 获取目录下的文件列表,过滤掉目录,同时去掉换行符 my @sdi_files = grep { -f "$sdipath/$_" } split /\n/, `ls -1 $sdipath`; my @spark_files = grep { -f "$sparkpath/$_" } split /\n/, `ls -1 $sparkpath`; # 把SDI文件的子串存到哈希里,方便快速查找 my %sdi_subs; foreach my $file (@sdi_files) { chomp $file; my $sub_str = substr($file, 0, -25); $sdi_subs{$sub_str} = $file; } # 检查Spark里哪些文件在SDI找不到匹配 print "Spark目录中无SDI匹配的文件:\n"; foreach my $file (@spark_files) { chomp $file; my $sub_str = substr($file, 0, -25); print "$file\n" unless exists $sdi_subs{$sub_str}; } # 反过来检查SDI里哪些文件在Spark找不到匹配 my %spark_subs; foreach my $file (@spark_files) { chomp $file; my $sub_str = substr($file, 0, -25); $spark_subs{$sub_str} = $file; } print "\nSDI目录中无Spark匹配的文件:\n"; foreach my $file (@sdi_files) { chomp $file; my $sub_str = substr($file, 0, -25); print "$file\n" unless exists $spark_subs{$sub_str}; }
代码解释
- 文件列表处理:用
ls -1确保每个文件占一行,split /\n/拆分结果,grep { -f ... }过滤掉目录(避免把目录当成文件处理),chomp去掉文件名末尾的换行符,保证子串截取准确。 - 哈希表快速查找:把其中一个目录的文件子串存入哈希,这样遍历另一个目录时,只用O(1)的时间就能检查是否存在匹配,比嵌套循环高效太多。
- 双向检查:既检查Spark里SDI没有的文件,也检查SDI里Spark没有的文件,覆盖所有不匹配的情况。
如果你想保留原代码结构的修复版本
要是你不想改哈希表的方式,只修复原代码的问题,可以用这个版本:
use strict; use warnings; my $sdipath="/home/ec2-user/TRF_DATA/FinancialStatement/FINALSDI"; my $sparkpath="/home/ec2-user/TRF_DATA/FinancialStatement/FINALSPARK"; my @a=`ls $sdipath`; my @b=`ls $sparkpath`; # 先把所有文件名的换行符去掉 chomp @a; chomp @b; my $flag; foreach (my $i = 0; $i < @a; $i++) { my $file_sub1=substr($a[$i],0,-25); $flag=0; # 把flag初始化移到外层循环,避免内层循环每次重置 for(my $j=0; $j < @b;$j++) { # 把<=改成<,避免索引越界 my $file_sub2=substr($b[$j],0,-25); if ($file_sub1 eq $file_sub2) { $flag=1; last; # 找到匹配就跳出内层循环,不用继续遍历 } } print "$a[$i]\n" if $flag == 0; } foreach (my $i = 0; $i < @b; $i++) { my $file_sub2=substr($b[$i],0,-25); $flag=0; for(my $j=0; $j < @a;$j++) { # 同样把<=改成< my $file_sub1=substr($a[$j],0,-25); if ($file_sub2 eq $file_sub1) { $flag=1; last; } } print "$b[$i] is missing in SDI file\n" if $flag == 0; }
这个版本主要修复了:
- 数组索引越界的问题(把
<=改成<) - 调整
$flag的初始化位置,避免内层循环每次重置导致错误 - 找到匹配后用
last跳出内层循环,提升效率 - 提前
chomp所有文件名,确保子串截取正确
内容的提问来源于stack exchange,提问作者nmkishore
相关产品推荐
相关产品推荐

