matching multiple line string with perl regex and doing a replace

前端 未结 4 2058
孤城傲影
孤城傲影 2021-01-24 00:06

I have a source file where I use a macro to do logging. Typical logging lines look like:

STDOUT_LOG(logDEBUG4) << \"a single line log text\" << aVari         


        
相关标签:
4条回答
  • 2021-01-24 00:13

    In the end the command that successfully did what I wanted was the following:

    perl -0777pne 's/STDOUT_LOG\((.*?)\)(.*?)<<(.*?);/STDOUT_LOG\(\1\,\2\3\);/gs' sensor.cpp
    

    This is alot a superposition for the answers of "Federico Piazza" and "zdim". You'll notice I prepended the "STDOUT_LOG" bit in the front to really much exactly what I wanted in a large number of source files. I noticed that for correct matching one MUST include the /s operator at the end of the regex , but also add the -0777 flag in the perl executable.

    see perlrun for an explanation of the runtime perl executable flags

    0 讨论(0)
  • 2021-01-24 00:27

    You can use a regex like this with s (single line flag):

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

    And the replacement string:

    , \1);
    

    Working demo

    0 讨论(0)
  • 2021-01-24 00:28

    With $log string containing the whole log, with newlines and all,

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

    The [^)]+ matches up to the first closing parentheses, and likewise [^;]+ goes to the first ;. You do not need nor want the /g modifier. The /x allows spaces for readability.

    The \K is a particular form of the positive lookbehind, which also discards all previous matches. So the pattern before it, used as an assertion, is not affected by the substitution. See it in perlretut. It is not essential here, but if you leave it out then STDOUT_LOG is consumed by the match and so it need be typed in as well in the replacement part. It's just cleaner this way.

    Since there could be a problem with how the log is input here is a complete program

    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;
    

    The do block allows us to slurp the whole file $logfile into the $log variable, by unsetting $INPUT_RECORD_SEPARATOR (or $/), within the block by local. See discussions in perlvar.


    If this is meant to be done with a one-liner

    perl -0777 -pe 's/STDOUT_LOG\K \( ([^)]+) \) ([^;]+);/($1,$2);/xs;' log.txt
    

    The -0777 makes it slurp the whole file into $_, and the substitution is by default done on it. The -p also prints $_ after each line is processed (and here we only have one "line" -- the whole file). See perlrun for command line switches and General Variables in perlvar for $_.


    Both these assume that the shown example of a log (one-liner or the multi-line one) stands on its own, that there is a single log-string to process. For simplicity I took that to be in a file log.txt and read it from that file, into the $log variable in the script and into $_ in the one-liner.

    0 讨论(0)
  • 2021-01-24 00:32

    Here is a way to do it:

    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;
    }
    

    Output:

    STDOUT_LOG(logDEBUG4, "a single line log text" << aVariable);
            STDOUT_LOG(logDEBUG4, 
                bsd->sensorName
                << " trainMinValueInWin = " << value.trough
                << " trainMaxValueInWin = " << value.peak
                << " |");
    
    0 讨论(0)
提交回复
热议问题