空字符串上的 Grep Perl 非贪婪作用域正则表达式匹配问题

Issue with Grep Perl Non-Greedy Scope RegEx Matching on Empty String

全部:

如主题所述,我 运行 遇到了 Grep Perl 非贪婪作用域正则表达式匹配空字符串的问题。

[注意:出于本示例的目的,假设 'title' 可以是复杂的、字母数字、特殊字符、多词、space 分隔的字符串。]

# echo "<span class=\"title\"></span><span class=\"price\">0.25</span><span class=\"title\">Banana</span><span class=\"price\">0.10</span><span class=\"title\">Grape</span><span class=\"price\">0.05</span>" | /opt/bin/grep -ioP "<span class=\"title\">(.+?)</span><span class=\"price\">(.+?)</span>" | sed "s/<span class=\"title\">//g; s/<span class=\"price\">/|/g; s/<\/span>//g;"
|0.25Banana|0.10
Grape|0.05

如您所见,第一个 'title' 匹配为空,但 grep perl 非贪婪范围正则表达式 (.+?) 仍然匹配。

不应该忽略第一个 'title' 匹配项吗?我错过了什么?

感谢您的帮助。

更新:

否定小于号 ([^<]+?) 是原始基本示例的一个很好的解决方案。但是,我发现当引入更多数据时它会遇到问题。

我已尝试扩展匹配以包括额外的尾随标记,但正则表达式似乎仍然因该更改而失败。

# echo "<span class=\"title\"></span></div></div><span class=\"price\">0.25</span><span class=\"title\">Banana</span></div></a><span class=\"price\">0.10</span><span class=\"title\">Grape</span></div></a><span class=\"price\">0.05</span>" | grep -ioP "<span class=\"title\">(.+?)</span></div></a><span class=\"price\">(.+?)</span>" | sed "s/<span class=\"title\">//g; s/<span class=\"price\">/|/g; s/<\/span>//g; s/<\/div>//g; s/<\/a>//g;"
|0.25Banana|0.10
Grape|0.05

正则表达式不应该匹配 </span></div></a> 标签,而不是 </span></div></div> 标签吗?

再次感谢您的宝贵时间和协助。

您选择的正则表达式 <span class="title">(.+?)</span> 假设在标题标签中至少存在一个符号 - 是什么导致正则表达式从这个地方捕获跳过空标签直到下一个结束 </span> 标签,绝对不是什么你打算实现。

也许下面的代码是不言自明的

use strict;
use warnings;

my $re = qr!<span class="title">(.+?)</span><span class="price">(.*?)</span>!;

my $input = do { local $/; <DATA> };
my %data = $input =~ /$re/g;

for my $k ( sort keys %data ) {
    printf "| %-10s | %6.2f |\n", $k, $data{$k};
}

__DATA__
<span class="title"></span><span class="price">0.25</span><span class="title">Banana</span><span class="price">0.10</span><span class="title">Grape</span><span class="price">0.05</span>

输出

| </span><span class="price">0.25</span><span class="title">Banana |   0.10 |
| Grape      |   0.05 |

也许您打算使用以下正则表达式

use strict;
use warnings;

my $re = qr!<span class="title">([^<]+?)</span><span class="price">(.*?)</span>!;

my $input = do { local $/; <DATA> };
my %data = $input =~ /$re/g;

for my $k ( sort keys %data ) {
    printf "| %-10s | %6.2f |\n", $k, $data{$k};
}

__DATA__
<span class="title"></span><span class="price">0.25</span><span class="title">Banana</span><span class="price">0.10</span><span class="title">Grape</span><span class="price">0.05</span>

输出

| Banana     |   0.10 |
| Grape      |   0.05 |

因此,如果您选择使用 grepsed 的方法,那么命令可能会遵循 shape

echo "<span class=\"title\"></span><span class=\"price\">0.25</span><span class=\"title\">Banana</span><span class=\"price\">0.10</span><span class=\"title\">Grape</span><span class=\"price\">0.05</span>" | grep -ioP "<span class=\"title\">([^<]+?)</span><span class=\"price\">(.+?)</span>" | sed "s/<span class=\"title\">//g; s/<span class=\"price\">/|/g; s/<\/span>//g;"

输出

Banana|0.10
Grape|0.05

如果 perl 在您的系统中可用,也许会更容易利用它的强大功能。

@PolarBear 成功!在您的指导下,我终于找到了针对我的特定问题的最佳解决方案,仍然使用原始的非贪婪范围正则表达式匹配 (.+?),它包括额外的前导标签,这些标签唯一地标识了我所针对的特定组同时排除那些不匹配的。感谢您的帮助和积极的反馈。