在Perl字符串中匹配美元符号

3
Perl程序中包含美元符号($)的简单文本字符串:
open my $fh, "<", $fp or die "can't read open '$fp': $OS_ERROR";
  while (<$fh>)
  {
    $line=''; #Initialize the line variable
    $line=$_; #Reading a record from a text file
    print "Line is $line\n"; #Printing for confirming
    (@arr)=split('\|',$line);
    

$line获取以下用竖线分隔的字符串(通过打印$line值确认):

Vanilla Cake $3.65 New Offering|Half pound Vanilla Cake||Cake with vanilla, cream and cheese

然后将该记录拆分并提取到特定的数组元素中:
(@arr)=split('\|',$line);
$arr[0]得到香草蛋糕$3.65,$arr1得到半磅香草蛋糕,$arr[2]保持空/NULL,$arr[3]得到香草、奶油和芝士蛋糕 现在我检查$arr[0]是否包含价格值。匹配的模式是一些文本(香草蛋糕),然后是美元符号($),接着是一个或多个数字(这种情况下是3),小数点是可选的 - 可能存在或可能不存在,然后小数点后面可以有一个或多个数字(在这种情况下是.65)。 使用以下正则表达式:
if ($arr[0]=~ /(.*?)(\$\d+(?:\.\d+)?)/)
{
     print "match1 is $1, match2 is $2, match3 is $3, match4 is $4\n";
}

问题在于$1、$2、$3、$4 - 所有匹配模式的值都打印为NULL/EMPTY。我认为这是因为$符号是字符串$ arr [0]的一部分。
我猜测,由于$3.65的值,它将$3部分(小数点前)视为变量并尝试替换它,而$3为空。因此,正则表达式匹配正在发生,但值提取可能失败,因为整个字符串可能被解释为Vanilla Cake .65,而不是Vanilla Cake $3.65(这是我的猜测)
可能,这就是为什么正则表达式匹配和提取失败的原因。
我还在某个地方读到过,它可能依赖于变量初始化($line或$arr [0]作为单引号或双引号)-我对这种依赖关系一无所知(这就是为什么像上面那样包括所有代码的原因)。 $line每次从文件中读取一条记录,因此需要在每次迭代时进行初始化。
我尝试了在变量中转义美元符号Perl中转义美元符号的麻烦解决方案中提供的解决方案,但无法使其正常工作。 在https://regex101.com/r/FQjcHp/2/上创建正则表达式的其他尝试也没有帮助。
请问有人可以告诉我如何使用正确的正则表达式代码从上述字符串中获取“Vanilla Cake”和“$3.65”的值吗?
附注:添加了一个在线编译器运行的截图,其中相同的代码可以正常工作并正确捕获$值。但不知何故,在我的程序中它没有捕获到。enter image description here

@NegativeZero - 包含更多的代码块。 - levent001
1
你只有两个捕获组... $3$4 将始终为空。 - Shawn
1
真的需要一个 [mcve]。使用 DATA 文件句柄而不是单独的输入文件来保持其自包含性。 - Shawn
我已经更新了更详细的代码块。不确定还能添加多少内容。请告诉我需要什么进一步的诊断。 - levent001
1
如果现在是你的责任,切换到严格/警告模式将为你节省很多后续麻烦。我知道有时候说服上级可能很难,但还是要尝试一下... - Ecuador
显示剩余11条评论
2个回答

4
这段代码
if ($foo =~ /(.*?)(\$\d+(?:\.\d+)?)/) {
     print "match1 is $1, match2 is $2, match3 is $3, match4 is $4\n";
}

有了这个输入

Vanilla Cake $3.65 

将打印

Use of uninitialized value $3 in concatenation (.) or string at ...
Use of uninitialized value $4 in concatenation (.) or string at ...
match1 is Vanilla Cake , match2 is $3.65, match3 is , match4 is

如果您没有启用use warnings,则警告将是静默的。

这是您提供的代码对此输入执行的操作。您还展示了它在您的截图中的操作。您在评论中说,它在您的家用电脑上不会执行此操作。我会说这是不可能的。

要么您的代码不同,输入不同,或者您的Perl安装不同(虽然这不太可能是问题所在)。实际上没有其他选择。

一个巨大的问题是您没有在代码中使用use strict; use warnings。这意味着您代码中的许多问题都被隐藏了。在您的情况下,最可能是拼写错误,例如:

$Iine = $_;
if ($line =~ /...../)  # <---- not the same variable

但你请求了八小时时间来更新你的代码,所以我猜八小时后我们就会知道了。


几个要点

  while (<$fh>)
  {
    $line=''; #Initialize the line variable
    $line=$_; #Reading a record from a text file
  • 你不需要“初始化”line变量。下一行代码将完全多余。
  • 那一行代码实际上没有从文件中读取记录,readline语句<$fh>正在执行这个任务。
  • 通常,你会将这一行代码写成:while (my $line = <$fh>)
  • 在你的打印语句中,$3$4永远无法保存值,因为你缺少必要的捕获组(...)。两个捕获组意味着只有$1$2会被赋值。

编写Perl代码时,应始终使用

use strict;
use warnings;

如果不这样做,它将无法帮助您,只会隐藏您的问题。

此外,请养成在尽可能小的范围内放置声明 (my $var) 的习惯。示例代码:

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

while (my $line = <DATA>) {
    my @x = split /\|/, $line;
    if ($x[0] =~ /(.*?)(\$\d+(?:\.\d+)?)/) {
        say "$1 is $2";
    }
}

__DATA__
Vanilla Cake $3.65 New Offering|Half pound Vanilla Cake||Cake with vanilla, cream and cheese

0
我在大约两年前遇到了类似的问题 - 在找到$符号的根本问题之前,我不得不苦思冥想超过5天。以下是我的经历:
美元正则表达式值没有打印出来 - 类似于您所观察到的情况。
很久以前某人编写的Perl代码使用双引号初始化了字符串变量。就像这样:
$string="This is some text";

它一直运行得很完美,直到我碰了它。 :-)

我的做法是在其中插入了一个变量,就像这样

$string="This is some $PriceVariableHavingDollarSign text";

然后我尝试在$string变量上运行一个美元符号匹配的正则表达式,希望能检测到美元符号。不完全是你想要做的,但非常类似,如下所示:

$string=~ /(.*?)(\$\d+(?:\.\d+)?)/

而且,无论是给出编译错误,还是在我尝试的不同正则表达式组合中完全没有捕获到美元符号。

所以我的答案兼建议是,在您的“冗长代码”中检查是否发生了类似的情况,即变量上的双引号可能导致问题。

在接收源值之前,如果可能的话,请尝试使用\转义$符号,就像这样(至少解决了我的问题)。 而不是

PriceVariableHavingDollarSign = "Cake is $3.5";

尝试一下

$PriceVariableHavingDollarSign ="Cake is \$3.5";

这里有一个关于Perl中双引号和单引号的差异的很好的解释。 https://www.effectiveperlprogramming.com/2012/01/understand-the-order-of-operations-in-double-quoted-contexts/

同时,你在问题、评论和图形中提供了详细的信息,做得非常好。这有助于你获得所有可能的角度、场景以及解决方案。


实际上,你的问题不是将一个变量插入到双引号字符串中。你的主要问题是没有使用 use strict; use warnings。因为如果这样做,你的问题将在10秒内得到解决。顺便说一下,这不是我的 DV。 - TLP
你关于 use strict; use warnings 是正确的。如果这是别人很久以前编写的代码,现在你需要升级/修复它,那么它会带来全新的挑战。 - Aquaholic
同意。在某个时候,从头开始重写整个东西会变得更容易。 - TLP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接