在正则表达式搜索中使用标量

时间:2014-05-20 09:51:14

标签: regex perl scalar

我写了这段代码:

my $id = shift;
my $file = shift;
unless(open (INFO, $file)) { print "cant open file\n"; return 0; }
#this is how i do it - i didn't copy the code directly last time:
while(my $line = <info>)
{
    if($line =~ /d\s+S+\s\Q$id\disk\d+s\d+/g)
    {
        print "yay i found it";
        close(INFO);
        return 1;
    }
}
close(INFO);
return 0;

一条好的线的例子是:

2:     Apple_HFS 0x123456789ABC   999.9 GB   disk2s2

(因为你可以看到$ id是&#34; 0x123456789ABC&#34;)

我的问题:它不起作用 - 它会打开文件并读取线条但是机器并不好。请告诉我,我在这里失踪了什么?我想我的正则表达式是错误的,但我无法解决它。

我试过谷歌和(当然)堆栈溢出(How to evaluate a word saved in a scalar via regular expression in Perl?Detect exact string value of scalar in regex matchingUse variable as RegEx pattern)但没有运气。 我确定我错过了一些基础知识,但这不是我的第一个正则表达式 - 只有冷杉才能有标量。

谢谢

3 个答案:

答案 0 :(得分:2)

当前的问题是$file是文件的名称。你打开它但从未真正读过它。

以下是对您的代码的进一步评论

  • 收集像这样的子程序的参数是很常见的,而且更加整洁

    my ($id, $file) = @_
    

    这也具有复制值的优点,因此调用中的实际参数不太可能被修改

  • 你应该使用open的三参数形式和词汇文件句柄,就像这样

    open my $fh, '<', $file
    

    特别是,当子例程退出时,文件将保持打开状态,因为您已选择了全局文件句柄。当词汇句柄超出范围时隐式关闭它们

  • 您应该使用$!错误消息中的open内置变量来提供有关 失败原因的信息

  • 错误通常由裸return表示,它返回undef或空列表,具体取决于上下文。列表上下文中的return 0会导致列表(0)生成 true 值,如果它被分配给数组

  • 除非确实需要能够一次访问所有文件,否则通常最好使用while循环逐行读取和处理它

  • /g正则表达式匹配修饰符用于查找字符串中所有出现的模式。如果你想要做的就是检查模式是否

  • ,这是不必要和浪费的。

你的正则表达式也有很多问题。如果我添加/x修饰符,那么我可以添加空格以更好地向您显示您所编写的内容

/ d \s+ S+ \s \Q$id \d isk \d+ s \d+ /x

匹配

  • 单个d字符
  • 一个或多个空格字符
  • 一个或多个S字符
  • 单个空格字符
  • \Q未终止,因此字符串的其余部分按字面匹配。如果您有\Q$id\E,那么模式的其余部分将匹配
  • 一位数
  • 字符串isk
  • 一个或多个数字
  • 单个s字符
  • 一个或多个数字

并不接近匹配您显示的记录格式。重要的是要记住,您的模式不需要匹配字符串的所有,因此您可能需要像/\b\Q$id\E\b/这样的内容来检查您的ID是否位于某个位置两端带字边界的字符串。我没有看到像0x123456789ABC这样的字符串出现在其他地方而且给出了误报

我认为最好的解决方案是在空白处拆分每条记录,并检查第三个字段是否与传入的ID匹配

您的子程序应如下所示

sub routine {
  my ($id, $file) = @_;

  open my $fh, '<', $file or do {
    warn "Unable to open '$file' for input: $!";
    return;
  };

  while (my $line = <$fh>) {
    my @fields = split ' ', $line;
    if ($fields[2] eq $id) {
      print "Yay! I found it!\n";
      return 1;
    }
  }

  return;
}

答案 1 :(得分:1)

而不是

my @lines = split(/\n/, $file);

my @lines = <INFO>;

甚至更好,

unless(open (my $INFO, "<", $file)) { print "cant open file\n"; return 0; }
while (my $line = <$INFO>)
{
  # ..
}

你还忘了结束字符串的引用即。 \Q$string\E

if($line =~ /d\s+S+\s\Q$id\Edisk\d+s\d+/g)

答案 2 :(得分:0)

我认为正则表达式是错误的。我不确定你想要匹配什么,所以我根据这个例子进行了尝试:

\d+:.*S\s+\Q$id\E.+disk\d+s\d+

这将匹配:

d+:一个数字后跟冒号

.*S\s+“Apple_HFS”中的“S”和空格

\Q$id\E您正在寻找的ID字符串

.+一切都达到'磁盘'

disk\d+s\d+ diskXXXsXXX

适用于此代码段:

$id = "0x123456789ABC";
$line = "2:     Apple_HFS 0x123456789ABC   999.9 GB   disk2s2";

if($line =~ /\d+:.*S\s+\Q$id\E.+disk\d+s\d+/g)
{
        print "yay i found it";
}