perl regex error: Modification of a read-only value attempted - regex

I have this perl script:
use strict;
use warnings;
foreach my $line (" ^?[?12;12A", " ^?[A") {
print "$line\n";
$line =~ s/\s?[[:cntrl:]]\[(\?)?([0-9]{1,2}(;[0-9]{1,2})?)?[a-zA-Z]//g;
print "$line\n";
}
Those are two strings that start with a space, then a control character, then some regular ascii characters. It results with this error:
$ perl foo.pl
[?12;12A
Modification of a read-only value attempted at foo.pl line 6.
$
What am I doing wrong?

In a foreach loop the loop variable ("topicalizer") is but an alias for the currently processed list element; by changing it we really change the element.
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
The loop in the question iterates over a list of string literals, and those are read-only. Attempting to change that is a fatal error (perldiag) (this case, of foreach, is given as one example.
Some ways around this are shown in Hameed's answer, to store them in an array, or to assign the string literal to a variable first.
Or, use the "non-destructive" modifier on the substitution operator, s///r, which doesn't change the original but returns the changed value (or the original if it didn't change)
my $new_line = $line =~ s/.../.../r;

In your case $line is a read-only value.
You can fix this in two ways:
Work with an actual array like my #testarray = (" ^?[?12;12A", " ^?[A");
Assign the value of $line to another variable and modify that:
my $tmp = $line;
$tmp =~ s/\s?[[:cntrl:]]\[(\?)?([0-9]{1,2}(;[0-9]{1,2})?)?[a-zA-Z]//g;

Related

How do I know if there is a number in my list?

My code looks like this:
#!/usr/bin/perl
$counter=0;
#list=<STDIN>;
chomp(#list);
if (#list==~ /^[+-]?\d+$/ )
{
$counter++;
}
print $counter;
So I write datas like: a b c d 1 2
And then it should print 2
beacuse of the 1,2
But it does not matter what datas i write into the list i get back 0.
So what is the problem with my if?
Always use strict; use warnings;. If your goal is to count the number of digit elements in your list, you can use grep to filter the list elements, then apply scalar to get its length (or use $length directly instead of storing the filtered list in #matches):
#!/usr/bin/perl
use strict;
use warnings;
my #list = <STDIN>;
chomp(#list);
my #matches = grep /^[+-]?\d+$/, #list;
print scalar #matches . "\n";
Sample run:
$ ./count.pl
-62
a
b
4
c
+91023
d
3
Honestly, it looks like you just guessed at this syntax. That's not really a great way to write a program :-)
Your main problems are on this line:
if (#list==~ /^[+-]?\d+$/ )
There are two pretty big problems here. Firstly, the #list. The match operator (=~) works on a single string at a time. So you need to use it on a scalar value. And if you give it an array (as you have done here) then Perl will (silently) evaluate the array as a scalar - which means that rather than getting the contents of the array, you'll get the number of elements in the array - which will be an integer so your regex will always match.
But, you say, it doesn't match. Yes, I realise that. And that's down to your second error - you have the match operator wrong. The operator is =~ and you're using ==~ (see the extra =). You would hope that an error like that cause a syntax error, but you've accidentally used a version that is syntactically valid but just doesn't do what you want. Perl interprets your code as:
if (#list = =~ /^[+-]?\d+$/ )
Note the spaces I've added. The one between the two = characters is important. This is saying "perform the match operation and assign the results to #list". But what is the match operator matching against? Well, it hasn't been given an explicit variable to match against, and in that case it matches against the default variable, $_. You haven't put anything into $_ so the match fails.
At this point, I should point out that if you had use warnings in your code, then you would be getting all sorts of useful warnings about what you're doing wrong. All Perl programmers (including the most experienced ones) should always have use strict and use warnings in their code.
There's another point of confusion here. It's the way you read your input.
#list = <STDIN>;
You haven't made it clear, but I suspect that you type in your list all in one line. In that case, you don't want to store your input in an array; you should store it in a scalar.
chomp($list = <STDIN>);
You can then convert it to a list (to be stored in an array) using split().
#list = split /\s+/, $list;
You can then get the count of numbers in your array using grep.
my $count = grep { /^[-+]\d+$/ } #list;
When evaluated as a scalar, grep returns the number of times the block of code was true.
Putting those together (and adding strict and warnings) we get the following:
#!/usr/bin/perl
use strict;
use warnings;
chomp(my $list = <STDIN>);
my $count = grep { /^[-+]\d+$/ } split /\s+/, $list;
Which, to my mind at least, looks simpler than your version.

Search two perl regex and store in one declared variable

So this is the scenario:
I perform a perl regex search on a string and store it in a variable
I then want to retrieve a substring from that variable and save it under the same variable
When I try with two separate variables it works, but I would like to minimies my variable declaration. What currently works:
my ($temp1) = ($buildLog =~ /(build\_version.*\d*)/);
my ($buildVersion) = ($temp1 =~ /(\d.*)/);
print "$temp1\n"; #$temp1 contains: build_version = 1411450178
print "$buildVersion\n"; #$buildVersion contains: 1411450178
But when I try to do it with one variable it only prints out the 1 ie that it found the match, but I would like the actual value. See below:
my ($temp2) = ($buildLog =~ /(build\_version.*\d*)/);
$temp2 = ($temp2 =~ /(\d.*)/);
print "$temp2\n"; #$temp1 just prints out 1
Could anybody please provide a quick explination of the behaviour and if it is indeed possible to use only one variable to get the content of the search ?
Thanks,
CJ
Only one regex is needed:
use strict;
use warnings;
my $buildLog = 'foobar build_version = 1411450178 bazbiz';
my ($buildVersion) = $buildLog =~ /build_version\D*(\d+)/;
print "$buildVersion\n";
Outputs:
1411450178
This answer has the correct solution for what you are trying to do. I just wanted to provide a quick explanation of why your code is not working as you expect.
You are confusing scalar and list modes. Your code
$temp2 = ($temp2 =~ /(\d.*)/);
is taking the results of the match (in a list context) and assigning it to a scalar. This assigns the number of elements in the list to the scalar.
You could also have used
$temp2 = ($temp2 =~ /(\d.*)/)[0];
to pick up the first match result.
#jm666's answer works because it assigns the list of match results to a list of variables.
The following
my ($temp2) = ($buildLog =~ /(build\_version.*\d*)/);
($temp2) = ($temp2 =~ /(\d.*)/);
will print
1411450178

extract first word from a sentence and store it

i am extracting first word from a line using regex in Perl
for my $source_line (#lines) {
$source_line =~ /^(.*?)\s/
}
But I want to store the first word into a variable
when I print the below code, I get correct output
print($source_line =~ /^(.*?)\s/)
when I want to store in $i and print it, I get output as 1.
my $i = ($source_line =~ /^(.*?)\s/);
print $i;
How do the store the first word into a temporary variable
You need to evaluate the match in list context.
my ($i) = $source_line =~ /^(.*?)\s/;
my ($i) is the same as (my $i), which "looks like a list", so it causes = to be the list assignment operator, and the list assignment operator evaluates its RHS in list context.
By the way, the following version works even if there's only one work and when there's leading whitespace:
my ($i) = $source_line =~ /(\S+)/;
It all comes down to context, this expression:
$source_line =~ /^(.*?)\s/
returns a list of matches.
When you evaluate a list in list context, you get the list itself back. When you evaluate a list in scalar context, you get the size of the list back; which is what is happening here.
So changing your lhs expression to be in list context:
my ($i) = $source_line =~ /^(.*?)\s/;
captures the word correctly.
There were recently a few articles on Perl Weekly related to context, here is one of them that was particularly good: http://perlhacks.com/2013/12/misunderstanding-context/

Can anyone explain me this regex meaning

I would like to understand this expression meaning.
$req_msg =~ s/ \${$toReplace}/$replacements->{$toReplace}/g;
Prerequisite for this to work are two variables:
$toReplace - contains an arbitrary value
$replacements - a HASH ref containing, erm, replacements
Given $toReplace contains "foo", the contents of $req_msq are searched for ${foo} (with a leading single space) with every occurence of this being replaced with $replacements->{foo}.
$req_msg =~ s/ \${$toReplace}/$replacements->{$toReplace}/g;
s is used for substitution. $content=~ s/old_value/new_value/modifier; (modifier can be i, g, x, along or combination)
Ex:
$content = "Hi I am a coder and I like coding very much!";
$content =~ s/i/eye/i;
now $content will contain "Heye eye am a coders and eye like coding very much"
In the same way ${$toReplace} which simply means a scalar reference is the old value which needs to be replace and $replacements->{$toReplace} means $replacements is a hash reference whose key is $toReplace .
It is smiliar to $hash_value = hash_ref->{key};
whereever it finds the value returned by scalar reference , gets replace by hash reference's key with the corresponding value found in $req_msg
But I guess you asked this question because you got blank replacement. That may be due to scalar reference problem.
This code snippet may help in removing your doubt.
#!/usr/bin/perl
use strict;
use warnings;
my $value = "Jassi";
my $scalar_ref = \$value;
print "scalar_ref = $scalar_ref \n and value = $value and ${$scalar_ref}\n";
my %hash = ("Jassi", "aliencoders");
my $hash_ref = \%hash;
my $reg_msg = "Hi this is Jassi";
print "reg_msg = $reg_msg \n";
$reg_msg =~ s/${$scalar_ref}/$hash_ref->{${$scalar_ref}}/;
print "reg_msg after s = $reg_msg\n";
See the second last line!
It replaces every occurance of the text ${blabla} with whatever is stored in the hash reference $replacements with the key blabla, e.g.:
$replacements = { 'blabla' => 'blubb' };
will make every ${blabla} being replaced by blubb in $req_msg.

Quote - capture - question

Could someone explain, why I can use $1 two times and get different results?
perl -wle '"ok" =~ /(.*)/; sub { "huh?" =~ /(.*)/; print for #_ }->( "$1", $1 )'
(Found in: How to exclude submatches in Perl?)
The #_ argument array doesn't behave the way you think it does. The values in #_ in a subroutine are actually aliases for the real arguments:
The array #_ is a local array, but its elements are aliases for the actual scalar parameters.
When you say this:
sub s {
"huh?" =~ /(.*)/;
print for #_;
}
"ok" =~ /(.*)/;
s("$1", $1);
The $1 in the first argument to s is immediately evaluated by the string interpolation but the second argument is not evaluated, it is just noted that the second value in the sub's version of #_ is $1 (the actual variable $1, not its value). Then, inside s, the value of $1 is changed by your regular expression. And now, your #_ has an alias for the string "ok" followed by an alias for $1, these aliases are resolved by the print in your loop.
If you change the function to this:
sub s {
my #a = #_;
"huh?" =~ /(.*)/;
print for #a;
}
or even this:
sub s {
local $1;
"huh?" =~ /(.*)/;
print for #_;
}
Then you'll get the two lines of "ok" that you're expecting. The funny (funny peculiar, not funny ha-ha) is that those two versions of s produce your expected result for different reasons. The my #a = #_; version extracts the current values of the aliases in #_ before the regular expression gets its hands on $1; the local $1; version localizes the $1 variable to the sub leaving the alias in #_ referencing the version of $1 from outside the sub:
A local modifies the listed variables to be local to the enclosing block, file, or eval.
Oddities like this are why you should always copy the values of the numbered regex capture variables to variables of your as soon as possible and why you want to unpack #_ right at the beginning of your functions (unless you know why you don't want to do that).
Hopefully I haven't butchered the terminology too much, this is one of those weird corners of Perl that I've always stayed away from because I don't like juggling razor blades.
The sample code makes use of two facts:
The elements of the #_ array are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (and vice versa).
$1 is a global variable (albeit dynamically scoped to the current BLOCK), which automatically contains the subpattern from () from the last successful pattern match.
The first argument to the subroutine is an ordinary string ("ok"). The second argument is the global variable $1. But it is changed by the successful pattern match inside the subroutine, before the arguments are printed.
That happens because perl passes parameters by reference.
What you are doing is similar to:
my $a = 'ok';
sub foo {
$a = 'huh?';
print for #_;
}
my $b = $a;
foo($b, $a)
When the sub foo is called, $_[1] is actually an alias for $a and so its value gets modified when $a is modified.