提问人:h q 提问时间:9/3/2023 最后编辑:h q 更新时间:9/4/2023 访问量:84
仅当组匹配时才进行多行字符串替换
multi-line string substitution only if group matches
问:
如何仅在组与模式匹配时才对多行字符串执行单行单语句替换?
我需要引用“值”(类似 YAML 的文档),如果它们包含或等于 .请考虑下面的(非工作)代码::
-
$data =~ s/^(\s*\S+): (.+)$/$1: '$2'/mg if $2 =~ /:/ || $2 =~ /^\-$/;
示例输入文本字符串
data:
normal: text
timestamp: Wed Aug 23 07:07:07 2023
time-zone: UTC +03:00, Daylight Saving: +0h
type: -
duration: 45h 8m 41s
所需输出
data:
normal: text
timestamp: 'Wed Aug 23 07:07:07 2023'
time-zone: 'UTC +03:00, Daylight Saving: +0h'
type: '-'
duration: 45h 8m 41s
工作代码 - 我想用更优雅的形式替换它
my @lines = split "\n", $data;
foreach my $i (0 .. $#lines) {
my $line = $lines[$i];
if ($line =~ /^(\s*\S+): (.+)$/) {
my $key = $1;
my $val = $2;
$lines[$i] = "$key: '$val'" if $val =~ /:/ || $val =~ /^\-$/; # quote invalids
}
}
$data = join "\n", @lines;
say $data;
答:
perl -wnlE'say s/^.+?:\s\K (.*[:-].*)/\x27$1\x27/rx' file.txt
\K
会删除所有以前的匹配项 (from ),因此它们会保留在字符串中,我们不必捕获它们并将它们放回去。是问题中使用的单引号。$&
\x27
修饰符让替换返回更改后的字符串(如果模式不匹配,则返回原始字符串),然后打印该字符串;原件未更改。参见 perlre 中的修饰符。输出可以重定向到文件中,/r
perl -wnlE'...' file.txt > out.txt
或者输入文件可以使用开关就地更改-i
perl -i.bak -wnlE'...' file.txt
该部分还使它使用该扩展名保存备份。请参阅 perlrun 中的开关。.bak
这假设感兴趣的模式始终包含在一行中。
不知道是否会称它为“优雅”......
正如问题中指出的,并在注释中澄清,输入是程序中的多行字符串,而不是文件。为了一次处理整个字符串,上面的正则表达式需要一次更改和不同的修饰符
use warnings;
use strict;
use feature 'say';
my $data = <<'EOF';
data:
normal: text
timestamp: Wed Aug 23 07:07:07 2023
time-zone: UTC +03:00, Daylight Saving: +0h
type: -
duration: 45h 8m 41s
EOF
$data =~ s/^.+?:[\t ]+\K (.*[:-].*) $/'$1'/gmx;
say $data;
现在我们需要一个文字空格而不是(在第一行之后),因为也匹配换行符,并且在第一行(后面没有任何内容)出错,使其向下搜索下一行。对于文字空格,它无法与换行符匹配,并且将放弃第一行并从下一行重新开始匹配。为了清楚起见,我喜欢文字空格 () 的字符类,然后还添加了一个制表符,所以.\s
:
\s
data:
^
[ ]
[\t ]
在这里,我们还需要将模式限制在一条线上,否则贪婪的人会啜饮更多。使用修饰符时,(和)应用于字符串内的行(如果没有修饰符,它们仅锚定整个字符串,而不是内部的行)。在这里,我们还需要继续浏览字符串,进行更改。.*
/m
$
^
/g
在程序内部,单引号不是问题,因为它们在命令行上,所以现在我们不需要对它们使用十六进制。'
我在这里使用 doc 来介绍多行字符串,带有单引号,因为我们显然想要文字文本。
或者,为了避免这些微妙之处,并且仍然逐行处理,将字符串分解为行,对每行运行正则表达式,然后通过连接换行符(如果需要)来重新组合
$data = join "\n",
map { s/^.+?:\s\K (.*[:-].*)$/'$1'/xr }
split /\n+/, $data;
这消除了可能的空行(在显示的示例数据中没有空行),因为 I 在所有连续 (with ).如果这是不希望的,请使用(否)和空行。split
\n
+
split /\n/
+
如果不需要将其重新组合成多行字符串 - 或者无论如何都需要单独的行 - 则赋值给数组(而不是 -ing 并赋值回 )。join
$data
现在我们再次需要修饰符,以便 in 中的块返回(更改的或原始的)字符串,而不是 (nor )。/r
map
/g
/m
评论
$data =~ s/^.+?:\s\K (.*[:-].*)/\x27$1\x27/rx;
Useless use of non-destructive substitution (s///r) in void context at ./test.pl line XX.
/r
s///
my $new_data = $data =~ s/.../.../r
/r
$data =~ s/^.+?:\s\K (.*[:-].*)/\x27$1\x27/x;
$data
评论
\r?\n