将多行字符串与perl正则表达式匹配并进行替换

时间:2016-11-28 19:11:39

标签: regex perl

我有一个源文件,我使用宏来进行日志记录。典型的日志记录行如下所示:

STDOUT_LOG(logDEBUG4) << "a single line log text" << aVariable;

还存在一些多行日志命令:

        STDOUT_LOG(logDEBUG4)
            << bsd->sensorName
            << " trainMinValueInWin = " << value.trough
            << " trainMaxValueInWin = " << value.peak
            << " |";

我们的想法是获取日志字符串(无论是单行还是多行)并将它们放在宏的括号中。

通常最终结果如下:

 STDOUT_LOG(logDEBUG4,"a single line log text" << aVariable);

        STDOUT_LOG(logDEBUG4,
            bsd->sensorName
            << " trainMinValueInWin = " << value.trough
            << " trainMaxValueInWin = " << value.peak
            << " |");

我正在尝试阅读与多行匹配相关的任何内容,包括perl中的while循环,/s amd /m运算符以及搜索和替换的潜在方法,但我距离正确匹配多行宏。我假设我需要/ s运算符以便&#34;。&#34;也匹配换行符,但我无法成功使用它。

模式的开头当然是&#34; STDOUT_LOG&#34;并且结束是第一次出现&#34;;&#34;然而很多行后来它出现了。

这种尝试

 perl -ne 's/STDOUT_LOG\((.*?)\)(.*?)\;/STDOUT_LOG($1,$2)\;/gs; print;'

设法获取日志级别(&#34; logDEBUG4&#34;在这种情况下)是&#34;排序&#34;单行日志命令的OK,但是即使我最后使用了/ s,其余行的匹配也会失败。

如何解决此问题?我应该使用哪些其他操作员?那里有什么好的教程吗?最终是否有一个可以挽救这一天的perl命令?

编辑: 因为没有任何建议为我开箱即用(模式匹配失败,因此没有任何改变),我想知道这里是否涉及一些更新的功能。无论如何,我的perl版本是5.18.2。我试图在linux cli中运行命令,如下所示:

perl -ne 's/pattern/replace/gs; print;' <filename>

我希望我在这里做错了什么

4 个答案:

答案 0 :(得分:1)

你可以使用s(单行标志)这样的正则表达式:

\).*?<<(.*?);

替换字符串:

, \1);

<强> Working demo

答案 1 :(得分:1)

$log字符串包含整个日志,包含换行符和所有

$log =~ s/STDOUT_LOG\K \( ( [^)]+ ) \) ( [^;]+ );/($1,$2);/xs;

[^)]+与第一个右括号匹配,同样[^;]+也会转到第一个;。您不需要也不需要/g修饰符。 /x允许空格以便于阅读。

\Kpositive lookbehind的一种特殊形式,它也会丢弃之前的所有匹配项。因此,用作断言之前的模式不受替换的影响。见in perlretut。这里不是必需的,但是如果你把它留下来,那么匹配就会消耗STDOUT_LOG,所以它也需要在替换部分输入。它就这么干净了。

由于日志输入方式可能存在问题,因此这是一个完整的程序

use warnings 'all';
use strict;

my $logfile = 'log.txt';
my $log = do {
    local $/; 
    open my $fh, '<', $logfile or die "Can't open $logfile: $!";
    <$fh>;
};
# print $log;

$log =~ s/STDOUT_LOG\K \( ([^)]+) \) ([^;]+);/($1,$2);/xs;

print $log;

do块允许我们通过取消设置$logfile(或{{1}将 slurp 整个文件$log添加到$INPUT_RECORD_SEPARATOR变量中} {},在local的块内。请参阅perlvar中的讨论。

如果这是用单线

来完成的
$/

perl -0777 -pe 's/STDOUT_LOG\K \( ([^)]+) \) ([^;]+);/($1,$2);/xs;' log.txt 使整个文件变为-0777,默认情况下对其进行替换。处理完每一行后,$_也会打印-p(这里我们只有一行)。有关命令行开关,请参阅perlrun; $_请参阅General Variables in perlvar

这两个都假设所显示的日志示例(单行或多行)独立存在,只需要处理一个日志字符串。为简单起见,我将其放在文件$_中并将其从该文件中读取到脚本中的log.txt变量中,然后读入单行中的$log

答案 2 :(得分:0)

这是一种方法:

use Modern::Perl;

my @lines = (
'STDOUT_LOG(logDEBUG4) << "a single line log text" << aVariable;',
'        STDOUT_LOG(logDEBUG4)
            << bsd->sensorName
            << " trainMinValueInWin = " << value.trough
            << " trainMaxValueInWin = " << value.peak
            << " |";'
);
for (@lines) {
    s/(STDOUT_LOG\(.*?)\)(\s*)<<(.*?);/$1, $2$3);/gs;
    say;
}

<强>输出:

STDOUT_LOG(logDEBUG4, "a single line log text" << aVariable);
        STDOUT_LOG(logDEBUG4, 
            bsd->sensorName
            << " trainMinValueInWin = " << value.trough
            << " trainMaxValueInWin = " << value.peak
            << " |");

答案 3 :(得分:0)

最终成功完成我想要的命令如下:

gh-pages

这对于#Federico Piazza&#34;的回答很有说服力。和&#34; zdim&#34;。您会注意到我在&#34; STDOUT_LOG&#34;之前添加了#34; STDOUT_LOG&#34;在前面的位置真的非常符合我想要的大量源文件。我注意到,为了正确匹配,必须在正则表达式的末尾包含perl -0777pne 's/STDOUT_LOG\((.*?)\)(.*?)<<(.*?);/STDOUT_LOG\(\1\,\2\3\);/gs' sensor.cpp 运算符,还要在perl可执行文件中添加/s标志。

有关运行时perl可执行标志的说明,请参阅perlrun