regexperl

Better way to swap two adjacent words in a Perl string?


I had an odd case where I typed in two names wrong in a game I was writing. They mirror each other. For instance, there are Henry Patrick and Patrick Henry, and I wanted to swap them in a mistaken chunk of code I wrote.

Now the Perl code below does so, but the temporary replacement string heinous-hack-string is, well, a hack. Is there a more elegant way to do things?

##################
#nameflip.pl
#
# this flips names where I mistakenly switched first-last and last-first
#

use strict;
use warnings;

my $mystr = "There's this guy named Henry Patrick, and there's another guy named Patrick Henry, " . 
  "and I confused them and need to switch them now!";

print "Before: $mystr\n";

print "After: " . stringNameSwitch($mystr, "Patrick", "Henry") . "\n";

##############################
# awful hack of a subroutine.
#
# what regex would do better?
#
# right now I just want to switch (name1 name2) <=> (name2 name1)

sub stringNameSwitch
{
  my $temp = $_[0];
  $temp =~ s/$_[1] +$_[2]/heinous-hack-string/i;
  $temp =~ s/$_[2] +$_[1]/$_[1] $_[2]/i;
  $temp =~ s/heinous-hack-string/$_[2] $_[1]/i;
  return $temp;
}

Solution

  • Like this, perhaps? The branch reset construct (?|...) allows the two names to be captured into $1 and $2 regardless of their order of appearance.

    use strict;
    use warnings 'all';
    
    my $mystr = <<END;
    There's this guy named Henry Patrick,
    and there's another guy named Patrick Henry,
    and I confused them and need to switch them now!
    END
    
    print name_switch($mystr, qw/ Henry Patrick /);
    
    sub name_switch {
        my ($s, $n1, $n2) = @_;
    
        $s =~ s/\b(?|($n1)\s+($n2)|($n2)\s+($n1))\b/$2 $1/gr;
    }
    

    output

    There's this guy named Patrick Henry,
    and there's another guy named Henry Patrick,
    and I confused them and need to switch them now!