RanRag
RanRag

Reputation: 49567

Perl Regex : Modification of a read-only value attempted

I am trying to replace whitespaces in a string after matching it against a regular expression.

my $string = "watch download Buffy the Vampire Slayer Season 1 Episode 1 Gorillavid";

if ($string =~ m!(Season\s\d+\sEpisode\s\d+)!){

    $1 =~ s!\s+!!g;

    say $1;

}

Now when I run the above code I get Modification of a read-only value attempted. Now if I store the value of $1 in a variable than try to perform a substitution on that variable it works fine.

So, is there any way I can perform a substitution in place without creating a new temporary variable.

PS: Can someone show me how to write the above code as one-liner because I am not able to :)

Upvotes: 3

Views: 1270

Answers (5)

Vijay
Vijay

Reputation: 67231

You can try this:

perl -pi -e 'if($_=~/Season\s\d+\sEpisode\s\d/){s/\s+//g;}' file

tested below:

XXX> cat temp
watch download Buffy the Vampire Slayer Season 1 Episode 1 Gorillavid
XXX> perl -pi -e 'if($_=~/Season\s\d+\sEpisode\s\d/){s/\s+//g;}' temp
XXX> cat temp
watchdownloadBuffytheVampireSlayerSeason1Episode1GorillavidXXX>

Upvotes: -1

TLP
TLP

Reputation: 67918

If you use a post-script for loop to create a local instance of $_, you can chain your substitution with a print (using a comma) to achieve a pre-print processing of the match.

Note that parentheses are not required when using the global /g option. Also note that this makes your if-statement redundant, since any non-match will return an empty list to your for loop.

perl -nlwe 's/\s+//g, print for /Season\s+\d+\s+Episode\s+\d+/g;' yourfile.txt

In your script, it looks like this. Note that the if-statement is replaced with a for loop.

for ( $string =~ /Season\s+\d+\s+Episode\s+\d+/g ) {
    s/\s+//g;  # implies $_ =~ s/\s+//g
    say;       # implies say $_

}

This is mainly to demonstrate the one-liner. You may insert a lexical variable instead of using $_, e.g. for my $match ( ... ) if you want increased readability.

Upvotes: 2

Zaid
Zaid

Reputation: 37146

$string =~ s{(?<=Season)\s*(\d+)\s*(Episode)\s*(\d+)}{$1$3$2};

Upvotes: 1

Borodin
Borodin

Reputation: 126722

It looks like you want to compress Season 1 Episode 1 to Season1Episode1 within the original string

This can be done conveniently using the @- and @+ together with a call to substr as an lvalue

This program shows the idea

use strict;
use warnings;

my $string = "watch download Buffy the Vampire Slayer Season 1 Episode 1 Gorillavid";

if ($string =~ /Season\s+\d+\s+Episode\s+\d+/ ) {

  substr($string, $-[0], $+[0] - $-[0]) =~ s/\s+//g;
  print $string;
}

output

watch download Buffy the Vampire Slayer Season1Episode1 Gorillavid

You don't say why you want to write this in a single line, but if you must then this will do it for you

perl -pe '/Season\s*\d+\s*Episode\s*\d+/ and substr($_, $-[0], $+[0] - $-[0]) =~ s/\s+//g' myfile

Upvotes: 3

Oleg V. Volkov
Oleg V. Volkov

Reputation: 22421

Don't mess with special variables and just capture the data you want, while construction output yourself.

$string = "watch download Buffy the Vampire Slayer Season 1 Episode 1 Gorillavid";
if ($string =~ m!Season\s(\d+)\sEpisode\s(\d+)!){
   say("Season$1Episode$2");
}

Upvotes: 5

Related Questions