我选择的文字如下所示。我需要对它做一个基本的编辑,但无法理解我需要的正则表达式。也许这只是漫长的一天,我没有看到我需要的东西。
示例数据:
START ITEM = 1235
BEGIN
WORD
RATE = 98
MORE WORDS
CODE = XX
STUFF
END
BEGIN
TEXT
MORE WORDS
RATE = 57
ADDITIONAL TEXT
CODE = YY
OTHER THINGS
END
STOP
START ITEM = 9983
BEGIN
WORD
RATE = 01
MORE WORDS
CODE = AA
STUFF
END
BEGIN
TEXT
MORE WORDS
RATE = 99
ADDITIONAL TEXT
CODE = XX
OTHER THINGS
END
STOP
我已获得CODE
和ITEM
个号码,需要在相应的BEGIN
/ END
部分中修改费率。幸运的是,这些部分定义明确,STOP
/ START
BEGIN
/ END
(他们是关键字,而不是其他任何地方)。
我的工具箱是Perl正则表达式。*
我尝试的第一个解决方案不起作用(值硬编码):
$tx =~ s/(START \s ITEM \s = \s 9983.*?
BEGIN
.*?
RATE \s = \s )\d+
(.*? # Goes too far
CODE \s = \s XX)
/$1$newRate$2
/sx;
因为指示的代码过多地匹配,找到更正确的代码,但总是编辑第一个条目。
建议?
*
实际代码依赖于将正则表达式添加到一堆正则表达式(一种后处理过滤器)上,每个正则表达式依次应用于要编辑的文本。哎呀,如果我有文本,我可以做一个全功能的解析器。但是我希望不要打开代码并坚持使用我已经拥有的API。
答案 0 :(得分:6)
正则表达式不适合这类问题。我推荐一个简单的迭代解决方案:
while (<FILE>) {
# push lines straight to output until we find the START that we want
print OUT $_;
next unless m/START ITEM = $number/;
# save the lines until we get to the CODE that we want
my @lines;
while (<FILE>)
{
push @lines, $_;
last if m/CODE = $code/;
}
# @lines now has everything from the START to the CODE. Get the last RATE in
# @lines and change its value.
my $strref = \( grep m/RATE/ @lines )[-1];
$$strref = $new_value;
# print out the lines we saved and exit the loop
print OUT @lines;
last;
}
编辑:如果你真的想要一个正则表达式,你可以使用这样的东西(未经测试):
$tx =~ s/(START \s+ ITEM \s+ = \s+ 9983.*?
BEGIN
.*?
RATE \s+ = \s+ )\d+
( (?: (?! END ) . )*
CODE \s+ = \s+ XX)
/$1$newRate$2
/sx;
添加的(?: (?! END ) . )*
确保RATE和CODE之间的匹配不会越过END。但这将比非正则表达方法慢得多。
答案 1 :(得分:4)
虽然我不喜欢它回溯多少,但在BEGIN
和RATE
之间进行捕捉贪婪将允许您跳至RATE
in CODE
= XX
的部分。像这样:
$tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+
BEGIN
.*
RATE \s+ = \s+ )\d+
...
这方面的主要问题是,如果有必要,它会跳转到另一个ITEM
,因此您必须确保不会吞噬STOP
。像这样:
my $tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+
BEGIN
(?: (?! \b STOP \b ) . )*
RATE \s+ = \s+ )\d+
(.*? # Goes too far
CODE \s+ = \s+ XX)
/msx
;
它仍然比我想要的还要多。
(一小时后)我意识到价值为RATE
的{{1}}和CODE
字段不能除以XX
。因此另一个解决方案是:
END
(我将其修改为仅在一行中查找END。如果my $tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+
BEGIN
.*?
RATE \s+ = \s+ )\d+
((?:(?! ^ \s+ END \s* $ ) . )*?
CODE \s+ = \s+ XX)
/msx
;
可以包含单个END,那么无论如何都难以解析)
我认为这个版本不会回溯太多,因为它只是从ADDITIONAL TEXT
开始,如果我们没有RATE =
,则会在CODE =
之前进行扫描END
{1}},然后它将修剪回到它认为匹配CODE = XX
的位置并继续寻找下一个RATE
。如果我们不知道Item#9983肯定会有代码'XX',我们可以为RATE
添加负前瞻。
已编辑以消除错误STOP
问题。
注意:现在需要以下部分:
\s
答案 2 :(得分:0)
正则表达式并不总是解析文本的最佳答案。您的示例显示您确实有一个可以用语法表示的文件。使用解析器提取字段然后对提取的信息进行更新将会更加简单。