Reputation: 6088
This is a stupid question; I need this for something more complicated but let's make it simple:
$i = quotemeta 'A';
$line =~ tr/a-z/$i-Z/;
This is a silly substitution, this should turn small letters to capital ones, but what ever I tried doesn't work.
Ok so I see this brought lots of confusion, the real code should be:
$line =~ tr/A-Z/F-ZA-E/;
It should be a Caesers Chiper algorhytm. This works just fine if you want to change it by a fixed number of letters, but I need a variable so I could change it as many number of letters as I want to. But I want to ship eval Thnx
P.S. Also, why doesn't s/// work with ranges?
Upvotes: 4
Views: 869
Reputation: 67910
Note that with tr///
, you can only use literal characters, not variables. So, tr/a-z/$i-Z/
does not work, you'd have to use tr/a-z/A-Z/
. In perldoc perlop you can read more about tr///
and also find the explanation on interpolation or lack thereof.
To turn a string into upper case, though there are other ways. You can use the uc
function, or the regex escape sequences \U
(upper case) and \L
(lower case). E.g.:
my $str = 'a';
my $uppercase = uc $str;
Regex:
my $str = 'abcdef';
$str =~ s/([a-z]+)/\U$1/; # $str is now 'ABCDEF'
tchrist points out that [a-z]
should be written \p{CWU}
, which will include all characters which are affected by case.
Update:
If you're trying to create some kind of dynamic encoding feature, I would suggest a subroutine that takes the arguments, and moves the characters around manually.
sub foo {
my ($str, $num) = @_; # num is how many characters to shift the alphabet
my @in = 'A' .. 'Z';
my @out = @in[$num .. $#in, 0 .. $num-1];
my %out = map { $in[$_] => $out[$_] } 0 .. $#in;
$str =~ s#(.)# $out{$1} // $1 #eg; # if key does not exist, use org
return $str;
}
Note that it is case sensitive, but you can add keys for lower case to solve that.
Upvotes: 4
Reputation: 95315
Your example is unfortunately not a good one; the tr
operator doesn't accept any variable interpolation (in either half). If you are actually using tr
, you are out of luck unless you resort to eval
, which is not recommended. If you're not using tr
, then please show us the actual code so we can see what's going on and offer more suggestions.
UPDATE
How about this?
my %map;
@map{'A'..'Z'} = ('F'..'Z','A'..'E');
s/[A-Z]/$map{$&}/g;
Not sure if it's still true, but once upon a time $&
was to be avoided at all costs due to a terrible performance penalty. You can do so easily enough:
s/([A-Z])/$map{$1}/g;
Upvotes: 2
Reputation: 7187
If you really need tr, you could use eval:
$i = quotemeta 'A'
eval("\$line =~ tr/a-z/$i-Z/;");
Upvotes: 4