【问题标题】:Check whether a field from a line of text line matches a value检查文本行中的字段是否与值匹配
【发布时间】:2018-05-08 09:13:53
【问题描述】:

我一直在使用以下 Perl 代码从多个文本文件中提取文本。它工作正常。

其中一个输入文件中的几行示例:

Fa0/19    CUTExyz     notconnect   129         half    100 10/100BaseTX
Fa0/22    xyz MLS     notconnect   1293        half     10 10/100BaseTX

我需要的是精确匹配每一行中的数字(即1291293 不匹配)并打印相应的行。

匹配一系列数字也会很好,将特定数字排除在外,即匹配 2 到 10,但不匹配 11 到 12 到 20

#!/perl/bin/perl

use warnings;

my @files = <c:/perl64/files/*>;

foreach $file ( @files ) {

    open( FILE, "$file" );

    while ( $line = <FILE> ) {
        print "$file $line" if $line =~ /123/n;
    }

    close FILE;
}

谢谢你的建议,但是可以使用上面的代码结构吗?

【问题讨论】:

  • /n 是什么意思?
  • 这似乎是 5.22 中的新事物,可以防止组捕获。但鉴于 OP 没有组,我认为这是一个错字。
  • 能否请edit 提供一些应该和不应该匹配的行示例?
  • 到目前为止,格式化代码最简单的方法是使用perltidy -pbp。无论如何,这是一个值得使用的工具,但如果您想在某处发布代码,则更是如此:)
  • 仅当它是“整行完全匹配”而不是“匹配行上的数字”时。

标签: perl


【解决方案1】:

我建议你看看perldoc perlre

您需要锚定您的正则表达式模式。最简单的方法可能是使用\b,它是字母数字和非字母数字之间的零宽度边界。

#!/perl/bin/perl
use warnings;
use strict;

foreach my $file ( glob "c:/perl64/files/*" ) {
   open( my $input, '<', $file ) or die $!;
   while (<$input>) {
      print "$file $_" if m/\b123\b/;
   }
   close $input;
}

注意 - 你应该使用三参数 open 和上面的词法文件句柄,因为这是更好的做法。

我还删除了 n 模式修饰符,因为它看起来是多余的。

在您进行编辑后,给我们一些源数据。我建议解决方案是 not 使用正则表达式 - 您的源数据看起来是空格分隔的。 (也许那些是标签?)。

所以我建议您最好使用split 并选择您想要的字段,并对其进行数字测试,因为您提到了匹配范围。这不适合正则表达式,因为它们不理解数字内容。

改为:

while ( <$input> ) {
   print if (split)[-4] == 129;
}

注意 - 我在 split 中使用 -4,它从列表的 end 开始索引。 这是因为第 3 列包含空格,所以除非我们从数组末尾倒数,否则在空格上拆分会产生错误的结果。使用负索引,我们每次都得到正确的字段。

如果您的数据是制表符分隔的,那么您可以使用chompsplit /\t/。或者可能在/\s{2,}/ 上拆分以拆分为 2 个或多个空格

但是通过选择字段,您可以对其进行数字测试,例如

if $fields[-4] > 100 and $fields[-4] < 200

等等

【讨论】:

  • \b 匹配单词边界;正则表达式/\b123\b/ 匹配“123 是新的 456”行。
  • @tripleee:看看(现在添加的)示例输入,这就是 OP 正在寻找的行为。
【解决方案2】:

我希望你没有得到你想要的答案,因为你不熟悉 Perl,所以放弃了最佳实践。问如何编写一个丑陋的解决方案是不合适的,因为正确的 Perl 超出你的能力范围

正如本网站反复所说的那样,如果您不知道如何做一份工作,那么您应该雇用知道的人并为他们的工作付费。我所知道的其他职业都没有期望免费完成高质量的工作

这里有一些关于您的代码的注释。无论您在哪里学习技术,您一直在寻找一个非常过时的资源

  • 真的有一个根目录perl,所以你的编译器是/perl/bin/perl吗?这很不寻常,在 Windows 中没有必要使用 shebang

  • 必须始终在您编写的每个 Perl 程序的顶部添加use strictuse warnings 'all',并使用@987654326 声明所有变量@尽可能接近他们的第一个使用点。出于某种原因,您使用@files 执行此操作,但不是使用$file

  • 最好将&lt;c:/perl64/files/*&gt; 替换为glob 'C:/perl64/files/*'。否则代码不太清楚,因为 Perl 重载了 &lt;&gt; 运算符

  • 不要将变量名放在双引号内。充其量是不必要的,并且可能会导致错误。所以"$file" 应该是$file

  • 始终使用open的三参数版本,这样第二个参数就是开启模式

  • 不要使用全局文件句柄。并且总是测试文件是否已正确打开,如果open 失败,则会显示包含$! 的消息——失败的原因

    open( FILE, "$file" )
    

    应该是这样的

    open my $fh, '<', $file or die qq{Unable to open "$file" for input: $!}
    
  • 不要对所有事情都依赖正则表达式模式。在这种情况下,split 似乎是更好的选择,或者如果您的记录具有固定宽度的字段,则可能是 unpack。在下面的解决方案中,我在“多个空格”上使用了split,但是如果您的真实数据与您显示的不同(制表符分隔?)那么这将不起作用

    请注意,Fa0/129 也将与您当前的方法匹配

此 Perl 程序过滤您的数据,打印第四个字段 $lines[3](由多个空格字符划定)在数值上等于 129 的行

当输入为单个文件 splitn.txt 时,会产生显示的输出,其中包含您问题中显示的数据

use strict;
use warnings 'all';

for my $file ( glob 'C:/perl64/files/*' ) {

    open my $fh, '<', $file or die qq{Unable to open "$file" for input: $!};

    while ( my $line = <$fh> ) {
        chomp;
        my @fields = split /\s\s+/, $line;
        print "$file $line" if $fields[3] == 129;
    }
}

输出

splitn.txt Fa0/19    CUTExyz     notconnect   129         half    100 10/100BaseTX

【讨论】:

  • 试过你的代码..没用...谢谢你顺便说一下在免费网站上真的很有帮助...“如果你不知道如何做一份工作那么你应该雇佣一个知道的人并支付他们的工作费用”
  • @normbeef:那么你对问题的描述是缺乏的。我证明了我的代码适用于您提供的 两行 数据,除非您更加坦率,否则显然不可能做得更好。 “没有工作”对于诊断目的完全没用,但我并不感到惊讶:正如我所说,您的数据是如何分隔的非常不清楚,我写的东西对您的错误有用例子。
  • @normbeef:你引用我的 的原因远非显而易见“如果你不知道如何做一份工作,那么你应该雇佣一个知道的人并支付他们的工作费用”。你不同意我的观点吗?当你不能修好你的车,或者你的房子要倒塌时,你会怎么做?您认为软件工程任务一文不值吗?
  • @normbeef:错误只能是数据格式。如果你想追求这个,那么你可以将一些真实数据复制并粘贴到pastebin.com 之类的东西上,并在此处分享链接,但显示看起来像你真正拥有的字符行是没有用的。我猜这些字段是制表符分隔的,但是您发布的数据中没有制表符。记录也有可能具有固定宽度的字段,在这种情况下,unpack 是首选工具。被批评而傲慢自大并不能解决问题。
  • 请直截了当。我当然不认为软件工程不重要或一文不值,但这个网站是一个开放的论坛,我向你保证远非如此。我无法理解对您来说似乎是第二天性的答案的深度,我已经拼凑起来(如您所见)并且我知道我需要更好地解释这项工作。我的剧本很差,但我所拥有的一切都对我有用。我真的希望能摆脱它,因为这个项目并不真正需要 1000% 的准确性,它更多的是一份管理报告。我会尝试重新审视它并再次澄清。
【解决方案3】:

您的问题不清楚。当你说:

我需要的是准确匹配每一行中的数字

这可能意味着几件事。这可能意味着每一行只包含一个您想要匹配的数字。在这种情况下,使用== 可能比使用正则表达式更好。或者这可能意味着您在一行上有很多文本,而您只想匹配完整的数字。在这种情况下,您应该使用\b(“单词边界”锚点)-/\b123\b/

如果您的问题更清楚(也许通过向我们提供示例输入),那么人们将不必猜测您的意思。

您的代码还有几点:

  • 始终包括use strictuse warnings
  • 始终检查来自open() 的返回值,并对失败采取适当的措施。
  • 使用词法文件句柄和 open() 的 3-arg 版本。
  • 无需在您的open() 通话中引用$file
  • 使用$_ 可以简化您的代码。
  • 除非您的正则表达式包含括号,否则匹配运算符上的 /n 无效。

将所有这些放在一起(并假设我对您问题的第二种解释是正确的),您的代码可能如下所示:

#!/perl/bin/perl

use strict;
use warnings;

my @files = <c:/perl64/files/*>;

foreach my $file (@files) {
  open my $file_h, '<', $file
    or die "Can't open $file: $!";

  while (<$file_h>) {
    print "$file $_\n" if /\b123\b/;
  }

  # No need to close $file_h as it is closed
  # automatically when the variable goes out
  # of scope.
}

【讨论】:

  • 你好戴夫,非常感谢你的回复是正确的,我需要匹配一行/行中的字符串,但打印整行..我已经尝试过这段代码,它工作得很好。但是,它确实在打印输出之间添加了似乎是附加线的内容。
  • 我想问一个与此相关的额外问题,但不确定是否要问一个新问题,因为我在这个网站上似乎遇到了麻烦。我还需要在每一行上开始匹配多个值:即在打印该行之前搜索并匹配值 1 和 2。
  • @normbeef:是的,不需要显式打印换行符,因为$_ 中已经有换行符。对此很抱歉,但这是一个足够简单的解决方法。
  • @normbeef:如果您有新问题要问,请发布新问题。但请先尝试自己解决问题。
猜你喜欢
  • 1970-01-01
  • 2022-09-24
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
相关资源
最近更新 更多