taiko
taiko

Reputation: 448

Perl regexp substitution - multiple matches

Friends,

need some help with substitution regex. I have a string

;;;;;;;;;;;;;

and I need to replace it by

;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;

I tried

s/;;/;\\N/;/g

but it gives me

;\N;;\N;;\N;;\N;;\N;;\N;;

tried to fiddle with lookahead and lookbehind, but can't get it solved.

Upvotes: 0

Views: 75

Answers (2)

Sobrique
Sobrique

Reputation: 53478

I wouldn't use a regex for this, and instead make use of split:

#!/usr/bin/env perl
use strict;
use warnings;

my $str = ';;;;;;;;;;;;;'; 

print join ( '\N', split ( //, $str ) );

Splitting on nulls, to get each character, and making use of the fact that join puts delimiters between characters. (So not before first, and not after last).

This gives:

 ;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;\N;

Which I think matches your desired output?

As a oneliner, this would be:

perl -ne 'print join ( q{\N}, split // )'

Note - we need single quotes ' rather than double around the \N so it doesn't get interpolated.

If you need to handle variable content (e.g. not just ; ) you can add grep or map into the mix - I'd need some sample data to give you a useful answer there though.

I use this for infile edit, the regexp suits me better

Following on from that - perl is quite clever. It allows you to do in place editing (if that's what you're referring to) without needing to stick with regular expressions.

Traditionally you might do

perl -i.bak -p -e 's/something/somethingelse/g' somefile

What this is doing is expanding out that out into a loop:

LINE: while (defined($_ = <ARGV>)) {
    s/someting/somethingelse/g;
}
continue {
    die "-p destination: $!\n" unless print $_;
}

E.g. what it's actually doing is:

  • opening the file
  • iterating it by lines
  • transforming the line
  • printing the new line

And with -i that print is redirected to the new file name.

You don't have to restrict yourself to -p though - anything that generates output will work in this way - although bear in mind if it doesn't 'pass through' any lines that it doesn't modify (as a regular expression transform does) it'll lose data.

But you can definitely do:

perl -i.bak -ne 'print join ( q{\N}, split // )'

And inplace edit - but it'll trip over on lines that aren't just ;;;;; as your example.

So to avoid those:

perl -i.bak -ne 'if (m/;;;;/) { print join ( q{\N}, split // ) } else { print }'

Or perhaps more succinctly:

perl -i.bak -pe '$_ = join ( q{\N}, split // ) if m/;;;/'

Upvotes: 4

Casimir et Hippolyte
Casimir et Hippolyte

Reputation: 89547

Since you can't match twice the same character you approach doesn't work. To solve the problem you can only check the presence of a following ; with a lookahead (the second ; isn't a part of the match) :

s/;(?=;)/;\\N/g

Upvotes: 4

Related Questions