只有当一个组匹配一个模式时,我才能对多行字符串执行一行单语句替换?
如果(类似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;
1条答案
按热度按时间8ftvxx2r1#
\K
删除了所有之前的匹配(来自$&
),所以它们留在字符串中,我们不必捕获它们并将它们放回去。\x27
用于问题中使用的单引号。/r
修饰符让替换返回更改后的字符串(如果模式不匹配,则返回原始字符串),然后打印该字符串;原来的没有改变。参见perlre中的修饰符。可以将输出重定向到文件中,也可以使用
-i
开关就地更改输入文件.bak
部分使它还可以使用该扩展名保存备份。请参见perlrun中的开关。这假设感兴趣的模式总是包含在一行中。
我不确定是否有人会称之为“优雅”。
正如问题中所指出的,并在注解中澄清,输入是程序中的多行字符串,而不是文件。为了一次处理整个字符串,上面的正则表达式需要一个修改和不同的修饰符
现在我们需要一个文字空间而不是
\s
(在第一个:
之后),因为\s
也匹配一个换行符,并且在第一行(data:
后面没有任何东西)失败,使它向下搜索下一行。如果有文字空格,它就不能匹配新行,并将放弃第一行,从下一个^
开始重新匹配。为了清楚起见,我喜欢用字符类来表示文字空间([ ]
),然后再添加一个制表符,所以是[\t ]
。这里我们还需要将模式限制在一条线上,否则贪婪的
.*
会吞下更多。使用/m
修饰符,$
(和^
)应用于字符串内部的行(如果没有修饰符,它们只锚定整个字符串,而不是内部的行)。在这里,我们还需要/g
来继续遍历字符串,进行更改。在程序内部,单引号
'
不是问题,因为它们在命令行上,所以现在我们不需要为它们使用十六进制。我用这里的文档来介绍多行字符串,用单引号,因为我们显然需要文字。
或者,为了避免这些微妙之处,仍然逐行处理,将字符串分成几行,对每一行运行regex,然后通过加入换行符(如果需要)重新组装。
这消除了可能的空行(在所示的样本数据中没有),因为在所有连续的
\n
上都有Isplit
(带有+
)。如果不希望这样,则使用split /\n/
(不使用+
),并保留空行。如果不需要将其重新组合成多行字符串--或者无论如何都需要单独的行--那么将其赋值给一个数组(而不是
join
-ing并赋值回$data
)。现在,我们再次需要
/r
修饰符,以便map
中的块返回(更改或原始)字符串,但不返回/g
(也不返回/m
)。