使用 perl 脚本逐行读取文件以获得完全匹配
Read a file line by line for an exact match using perl script
我写了一个 perl
脚本来为给定的搜索字符串逐行读取输入文件。我已经使用内置 perl
函数 grep
和 index
完成了两个实现,但我无法获得精确字符串匹配的输出。
我的示例代码、输入文件和所需的输出如下所示。请帮助我理解此脚本的问题,这可以帮助我获得所需的输出。
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
有一个字符串 1234
,grep
或 index
检查不应该通过第二个搜索条件输入文件 list.txt
中的行 12345
.
我该如何解决这个问题以获得完全匹配?
您的问题是您正在进行的匹配,grep /$var/
和 index($line, $var)
都允许行部分匹配。即
12345
^^^^ <---- matches 1234
与 /car/
与 carpet
或 scarlet
部分匹配的方式大致相同。
您可能应该做的是隔离数字,将它们放在一个数组中,然后用数字来检查它。例如:
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.txt
或 cat 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
如果第 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
我写了一个 perl
脚本来为给定的搜索字符串逐行读取输入文件。我已经使用内置 perl
函数 grep
和 index
完成了两个实现,但我无法获得精确字符串匹配的输出。
我的示例代码、输入文件和所需的输出如下所示。请帮助我理解此脚本的问题,这可以帮助我获得所需的输出。
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
有一个字符串 1234
,grep
或 index
检查不应该通过第二个搜索条件输入文件 list.txt
中的行 12345
.
我该如何解决这个问题以获得完全匹配?
您的问题是您正在进行的匹配,grep /$var/
和 index($line, $var)
都允许行部分匹配。即
12345
^^^^ <---- matches 1234
与 /car/
与 carpet
或 scarlet
部分匹配的方式大致相同。
您可能应该做的是隔离数字,将它们放在一个数组中,然后用数字来检查它。例如:
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.txt
或 cat 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
如果第 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