使用 perl 脚本逐行读取文件以获得完全匹配

Read a file line by line for an exact match using perl script

我写了一个 perl 脚本来为给定的搜索字符串逐行读取输入文件。我已经使用内置 perl 函数 grepindex 完成了两个实现,但我无法获得精确字符串匹配的输出。

我的示例代码、输入文件和所需的输出如下所示。请帮助我理解此脚本的问题,这可以帮助我获得所需的输出。

SAMPLE_CODE

#!/usr/bin/perl

my $myfile = "/path/to/the/file/list.txt";
my $details = "1234,5678";
my @required;

open FH, "$myfile" or die "Cannot open file for reading\n";
while(<FH>)
{
    $line = $_;
    chomp $line;
    @list = split(/\,/, $details);

    foreach my $var (@list)
    {
        chomp($var);
        #if (grep /$var/, $line)            # partially working
        if (index($line, $var) >= 0)        # partially working
        {
            my @arr = split(/[\:]/, $line);
            push (@required, $arr[0]);
        }
    }
}
close FH;

print "required array is @required \n"; 

INPUT_FILE

$>  cat /path/to/the/file/list.txt

CAT:1234,5678
RAT:12345,9871

输出

required array is CAT CAT RAT 

DESIRED_OUTPUT

required array is CAT CAT

这里的问题是,由于变量 $details 有一个字符串 1234grepindex 检查不应该通过第二个搜索条件输入文件 list.txt 中的行 12345.

我该如何解决这个问题以获得完全匹配?

您的问题是您正在进行的匹配,grep /$var/index($line, $var) 都允许行部分匹配。即

12345
^^^^  <---- matches 1234

/car/carpetscarlet 部分匹配的方式大致相同。

您可能应该做的是隔离数字,将它们放在一个数组中,然后用数字来检查它。例如:

my ($name, @nums) = split /[:,]/, $line;     # split into all fields at once
for my $num (@nums) {
    for my $num2 (@list) {
        if ($num == $num2) {                 # check numerical equality
             push @required, $name;
        }
    }
}

或者如果正如您的评论所暗示的那样,您的字段是字符串,您可以使用 eq 来检查相等性。或者在正则表达式 /^$var$/ 中使用锚点来强制完全匹配。 ^表示行首,$表示行尾。例如:

"car" eq "carpet"     # false
"car" eq "car"        # true
"carpet" =~ /^car$/   # false

更有效的是,您可以对要测试的数字使用哈希,例如

my %list = map { $_ => 1 } split /,/, $details;
...
if ($list{$num}) {        # check if the value is true
    push @required, $name;
}

已经表明您的代码将匹配部分模式,这不是您想要的。您需要实现精确匹配,并且正则表达式有 \b 来指示元素的边界。

在脚本开头包含是一个好习惯

use strict;
use warnings;

什么可以警告您代码的不良影响。

对于这种情况,也许您可​​以使用 <>(空 handle/diamond 运算符)而不是打开文件句柄,它简化了代码并允许代码的双重用法,如 script.pl list.txtcat list.txt | script.pl

注意:@list = split(/,/, $details); 应该放在循环之外以节省 CPU 个循环

请查看以下生成所需输出的代码片段

#!/usr/bin/env perl
#
# vim: ai ts=4 sw=4

use strict;
use warnings;
use feature 'say';

my $details = "1234,5678";
my(@list, @required);

@list = split(/,/, $details);

while(<>) {
    for my $element ( @list ) {
        if( /\b$element\b/ ) {
            my @arr = split(/[:,]/, $_);
            push @required, $arr[0];
        }
    }
}

say "Required array is @required";

输出

Required array is CAT CAT

参考: <>, $_, split

如果第 2、第 3 等字段符合特定条件,您需要打印每行的第 1 个字段吗?

echo 'CAT:1234,5678\nRAT:12345,9871' |
perl -F'/:|,/' -lane 'foreach (@F) { push @required, $F[0] if /\b1234\b|\b5678\b/ }
                      print "The required array: @required"'

输出:

The required array: CAT CAT

-F'/:|,/' 选项告诉 Perl 在 :, 上拆分一行的字段,并用这些字段填充特殊数组 @F$F[0] 获取第一个字段,$F[1] 获取第二个字段,依此类推。

如果行的任何字段:foreach (@F) 匹配 1234 或 5678:if /\b1234\b|\b5678\b/,则将行的第一个字段压入 @required 数组:push @required, $F[0] .

从文件中读取数据:

perl -F'/:|,/' -lane 'foreach (@F) { push @required, $F[0] if /\b1234\b|\b5678\b/ } 
                      END{ print "The required array: @required" }' yourData.txt