Perl REGEX substitute every ! with ~ only when surrounded by quotes

Viewed 76

I'm scripting a conversion from Fortran 77 to Fortran 90, and trying to use PERL REGEX to substitute every ! with a ~ if found within quotes.

THIS> 'Hello !!!! Hello !!!!' becomes> 'Hello ~~~~ Hello ~~~~'

THIS> 'Hello' !!!! 'Hello !!!!' becomes> 'Hello' !!!! 'Hello ~~~~'

I can't figure out how to use transliteration cause it has to be only when in quotes The best I've been able to do is:

perl -pi -e "s/('.*?)\!+(.*')/\1~\2/g" File.txt

But this replaces 4 !s with 1 ~ and doesn't even change the second set of !s

THIS> 'Hello !!!! Hello !!!!' becomes> 'Hello ~ Hello !!!!'

THIS> 'Hello' !!!! 'Hello !!!!' becomes> 'Hello' !!!! 'Hello ~'

Without the + after the \! it replaces just the first ! anyway so you get 'Hello ~!!!'

I can't seem to be able to count each ! and replace it one for one with a ~. And I thought the Global /g would get both sets of !s but no.

Any thoughts?

2 Answers

Matching inside quotes is difficult. This is a little easier as there's only one type of quote. Assuming we have to content with \' escapes, we can use a negative look behind to look for unescaped quotes: (?<!\\)'. /s ensures we capture regardless of newlines. /g to capture every match in the string.

Next trick is to capture all the text between the quotes and process only that. We can use /e to make the replacement some Perl code that does a simple translation on the captured text: tr/!/~/.

use strict;
use warnings;
use v5.10;

my $str = qq['Hello' !!!! 'Hello !!!! I can\\'t hear you!\n'];

$str =~ s{
    (?<!\\)' (.*?) (?<!\\)'
}{
    my $inner = $1;
    $inner =~ tr/!/~/;
    "'$inner'";
}egmsx;

# 'Hello' !!!! 'Hello ~~~~ I can\'t hear you~
# '
say $str;

I think a parser rather than REGEX would be the better answer.
Schwern your answer seems to get everything in steps, but unless I'm reading it wrong it is not updating the original file in place.

Thanks for the suggestions but in the end, I used a 3rd approach. After some evaluation I determined that the case I was trying to prevent by changing the !'s, would not occur as often as I thought. So after running all the 500K lines of code through my converter - I went back and fixed by hand the half dozen cases where this problem occurred. Thanks for the logic exercises though.

Related