Perl s/$array[1]\b/$array[1] won't replace - perl

I would like to replace the string $array[1] by the actual variable value.
the \b doesn't seem to work
Does anyone know how the replace the array variable ? What's the delimiter ?
s/$array[1]\b/$array[1]

The [ ... ] has a special meaning in regular expressions (it defines a "character class"). If you want to use [ to mean a [, then you need to escape it with a \.
s/\$array\[1]/$array[1]/
Update: Added escape to $. Removed \b.

I would recommend a real templating engine to perform such a substitution, this will allow you to extend it to things that don't look exactly like $array[1] without making it more complicated, but you will need to alter your input to what the templating engine expects. One option is Text::Template.
use strict;
use warnings;
use Text::Template 'fill_in_string';
my $input = 'foo {$array[1]} bar';
my #array = 1..10;
my $rendered = fill_in_string $input, HASH => {array => \#array};
print $rendered, "\n"; # foo 2 bar

Related

How to do I convert an escaped t into a tab character

I have a variable that contains a slash and a t.
my $var = "\\t";
I want to convert that to a tab. How do I do that?
use Data::Dumper;
use Term::ReadLine;
my $rl = Term::ReadLine->new();
my $var = $rl->readline( 'Enter \t:' );
print Dumper $var;
The following is the simplest solution:
$var = "\t" if $var eq "\\t";
If you want to do this no matter where the sequence appears in the string, you could use
$var =~ s/\\t/\t/g;
But it sounds like you're not asking the right question. Nothing supports \t and nothing else. At the very least, I would also expect \\ to produce \. Are you perhaps trying to parse JSON? If so, there are number of other escape sequences you need to worry about.

Perl - Convert integer to text Char(1,2,3,4,5,6)

I am after some help trying to convert the following log I have to plain text.
This is a URL so there maybe %20 = 'space' and other but the main bit I am trying convert is the char(1,2,3,4,5,6) to text.
Below is an example of what I am trying to convert.
select%20char(45,120,49,45,81,45),char(45,120,50,45,81,45),char(45,120,51,45,81,45)
What I have tried so far is the following while trying to added into the char(in here) to convert with the chr($2)
perl -pe "s/(char())/chr($2)/ge"
All this has manage to do is remove the char but now I am trying to convert the number to text and remove the commas and brackets.
I maybe way off with how I am doing as I am fairly new to to perl.
perl -pe "s/word to remove/word to change it to/ge"
"s/(char(what goes in here))/chr($2)/ge"
Output try to achieve is
select -x1-Q-,-x2-Q-,-x3-Q-
Or
select%20-x1-Q-,-x2-Q-,-x3-Q-
Thanks for any help
There's too much to do here for a reasonable one-liner. Also, a script is easier to adjust later
use warnings;
use strict;
use feature 'say';
use URI::Escape 'uri_unescape';
my $string = q{select%20}
. q{char(45,120,49,45,81,45),char(45,120,50,45,81,45),}
. q{char(45,120,51,45,81,45)};
my $new_string = uri_unescape($string); # convert %20 and such
my #parts = $new_string =~ /(.*?)(char.*)/;
$parts[1] = join ',', map { chr( (/([0-9]+)/)[0] ) } split /,/, $parts[1];
$new_string = join '', #parts;
say $new_string;
this prints
select -x1-Q-,-x2-Q-,-x3-Q-
Comments
Module URI::Escape is used to convert percent-encoded characters, per RFC 3986
It is unspecified whether anything can follow the part with char(...)s, and what that might be. If there can be more after last char(...) adjust the splitting into #parts, or clarify
In the part with char(...)s only the numbers are needed, what regex in map uses
If you are going to use regex you should read up on it. See
perlretut, a tutorial
perlrequick, a quick-start introduction
perlre, the full account of syntax
perlreref, a quick reference (its See Also section is useful on its own)
Alright, this is going to be a messy "one-liner". Assuming your text is in a variable called $text.
$text =~ s{char\( ( (?: (?:\d+,)* \d+ )? ) \)}{
my #arr = split /,/, $1;
my $temp = join('', map { chr($_) } #arr);
$temp =~ s/^|$/"/g;
$temp
}xeg;
The regular expression matches char(, followed by a comma-separated list of sequences of digits, followed by ). We capture the digits in capture group $1. In the substitution, we split $1 on the comma (since chr only works on one character, not a whole list of them). Then we map chr over each number and concatenate the result into a string. The next line simply puts quotation marks at the start and end of the string (presumably you want the output quoted) and then returns the new string.
Input:
select%20char(45,120,49,45,81,45),char(45,120,50,45,81,45),char(45,120,51,45,81,45)
Output:
select%20"-x1-Q-","-x2-Q-","-x3-Q-"
If you want to replace the % escape sequences as well, I suggest doing that in a separate line. Trying to integrate both substitutions into one statement is going to get very hairy.
This will do as you ask. It performs the decoding in two stages: first the URI-encoding is decoded using chr hex $1, and then each char() function is translated to the string corresponding to the character equivalents of its decimal parameters
use strict;
use warnings 'all';
use feature 'say';
my $s = 'select%20char(45,120,49,45,81,45),char(45,120,50,45,81,45),char(45,120,51,45,81,45)';
$s =~ s/%(\d+)/ chr hex $1 /eg;
$s =~ s{ char \s* \( ( [^()]+ ) \) }{ join '', map chr, $1 =~ /\d+/g }xge;
say $s;
output
select -x1-Q-,-x2-Q-,-x3-Q-

Seeing value of Perl variable created at runtime

My code is below:
use strict;
my $store = 'Media Markt';
my $sentence = "I visited [store]";
# Replace characters "[" and "]"
$sentence =~ s/\[/\$/g;
$sentence =~ s/\]//g;
print $sentence;
I see following at screen:
I visited $store
Is it possible to see following? I want to see value of $store:
I visited Media Markt
You seem to be thinking of using a string, 'store', in order to build a variable name, $store. This gets to the subject of symbolic references, and you do not want to go there.
One way to do what you want is to build a hash that relates such strings to corresponding variables. Then capture the bracketed strings in the sentence and replace them by their hash values
use warnings;
use strict;
my $store = 'Media Markt';
my $time = 'morning';
my %repl = ( store => $store, time => $time );
my $sentence = "I visited [store] in the [time]";
$sentence =~ s/\[ ([^]]+) \]/$repl{$1}/gex;
print "$sentence\n";
This prints the line I visited Media Markt in the morning
The regex captures anything between [ ], by using the negated character class [^]] (any char other than ]), matched one-or-more times (+). Then it replaces that with its value in the hash, using /e to evaluate the replacement side as an expression. Since brackets are matched as well they end up being removed. The /x allows spaces inside, for readibilty.
For each string found in brackets there must be a key-value pair in the hash or you'll get a warning. To account for this, we can provide an alternative
$sentence =~ s{\[ ([^]+) \]}{$repl{$1}//"[$1]"}gex;
The defined-or operator (//) puts back "[$1]" if $repl{$1} returns undef (no key $1 in the hash, or it has undef value). Thus strings which have no hash pairs are unchanged. I changed the delimiters to s{}{} so that // can be used inside.
This does not allow nesting (like [store [name]]), does not handle multiline strings, and has other limitations. But it should work for reasonable cases.
As I told you on the Perl Programmers Facebook group, this is very similar to one of the answers in the Perl FAQ.
How can I expand variables in text strings?
If you can avoid it, don't, or if you can use a templating system, such as Text::Template or Template Toolkit, do that instead. You might even be able to get the job done with sprintf or printf:
my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a full templating system, I'll use a string that has two Perl scalar variables in it. In this example, I want to expand $foo and $bar to their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';
One way I can do this involves the substitution operator and a double /e flag. The first /e evaluates $1 on the replacement side and turns it into $foo. The second /e starts with $foo and replaces it with its value. $foo, then, turns into 'Fred', and that's finally what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The /e will also silently ignore violations of strict, replacing undefined variable names with the empty string. Since I'm using the /e flag (twice even!), I have all of the same security problems I have with eval in its string form. If there's something odd in $foo, perhaps something like #{[ system "rm -rf /" ]}, then I could get myself in trouble.
To get around the security problem, I could also pull the values from a hash instead of evaluating variable names. Using a single /e, I can check the hash to ensure the value exists, and if it doesn't, I can replace the missing value with a marker, in this case ??? to signal that I missed something:
my $string = 'This has $foo and $bar';
my %Replacements = (
foo => 'Fred',
);
# $string =~ s/\$(\w+)/$Replacements{$1}/g;
$string =~ s/\$(\w+)/
exists $Replacements{$1} ? $Replacements{$1} : '???'
/eg;
print $string;
And the actual (but really not recommended - for the reasons explained in the FAQ above) answer to your question is:
$sentence =~ s/\[(\w+)]/'$' . $1/ee;

Extracting text in between strings in Perl

In Perl, how do I extract the text in a string if I knew the pre and post parts of the text?
Example:
Input: www.google.com/search?size=1&make=BMW&model=2000
I would like to extract the word 'BMW' which is always in between "&make=" and the next "&"
Don't use a regular expression. Use URI and URI::QueryParam, like so:
use strict;
use warnings;
use URI;
use URI::QueryParam;
my $u = URI->new('http://www.google.com/search?size=1&make=BMW&model=2000');
print $u->query_param('make');
Use a Regular expression:
my ($captured_string) = $link =~ /\&make=(\w+)\&/;
My regex assumes that you would want to capture anything that appeared in the make field. \w captures upper and lower case letters. If you want to capture something else you can use a character class. Like this [\w\s]+ would match more than one letters and spaces. You can add anything between the [ ] of characters to match in any order.
The ( ) is what actually does the capturing. If you remove that then it will just match (and you should use it in an if statement. If you wanted capture more than one string (say you wanted the model as well. Based on your example you could use a second set of parenthesis like this:
my ($make, $model) = $link =~ /\&make=(\w+)\&model=([A-Za-z0-9]+)/;
Hope that helps!

Can I use the y operator to do a non-one-to-one transliteration in Perl?

The y operator in Perl does character-by-character transliteration. For example, if we do y/abc/dfg to the string "foobar", we get "foofdr". But what if I want to transliterate "ā" to "ei" and "ä" to "a:" and "ō" to "әu" and "o" to "ɒ".
I tried the following line of code but no luck:(
y/āäōo/(ei)(a:)(әu)ɒ/
Do we hopefully have a workaround for this problem? Or do I have to repeatedly use the s operator and do a lot of cumbersome substitutions?
Thanks in advance for any guidance :)
In this case, create a hash and go from the keys to the strings easily.
use warnings;
use strict;
use utf8;
binmode STDOUT, ":utf8";
my $string = "āäōo";
my %trans = qw/ā ei ä a: ō u o ɒ/;
my $keys = join '', keys %trans;
$string =~ s/([$keys])/$trans{$1}/g;
print "$string\n";
You need to alter this if your keys are more than one character long by sorting the keys in order of decreasing length and joining them using ( | | ) instead of [ ].
It sounds like you're trying to do something similar to Text::Unaccent::PurePerl.