user3791998
user3791998

Reputation: 69

Unable to substitute 0 with L and 1 with H in a Perl string

I am grappling with just a simple piece of code and unable to troubleshoot my way through. All I am trying to do is substitute 0 with L and 1 with H for the binary equivalent of a hex number. I need to get the hex number from the user, convert it to binary, scan through each bit of the binary and replace 0's with L's and 1's with H's. Simple requirement yet finding it hard to resolve.

use warnings;
use strict;

my $i2c = "80";
my $binary_string = sprintf "%08b", hex($i2c);
print "$binary_string \n";

my $charz = sprintf "%s", $binary_string;
print "$charz\n";

for (my $i=1; $i < @charz; $i++) {
  if (substr($charz, $i) eq "0")
    substr($charz, $i) = "L";
  else if (substr($charz, $i) eq "1")
    substr($charz, $i) = "H";
}

My output should be like this:

input hex = 80 

Therefore binary will be 10000000. So my output has to be HLLLLLLL.

I am not able to get this simple output. I tried a wide range of things to make it work (regex match and replace substring, index etc). I am not sure if I am making a basic error somewhere. Kindly help me out on this one.

The error that I get is this:

syntax error at temp3.pl line 12, near ")
substr"
Execution of temp3.pl aborted due to compilation errors.

Upvotes: 1

Views: 133

Answers (5)

Dave Cross
Dave Cross

Reputation: 69284

Running your code as given, I get the error 'Global symbol "@charz" requires explicit package name at string line 11'.

And that's true, because you're using the array @charz (in your for loop condition) without defining it anywhere. The fact that you don't mention this error in your question makes me think that you're not showing us the code that you're actually running. At the very least, you don't have use strict turned on in your real code.

If I comment out the use strict line, I get the syntax error that you mentioned. That's caused by the missing braces that other people have pointed out. We can also tidy things up a bit by using elsif in place of else if.

for (my $i=1; $i < @charz; $i++) {
  if (substr($charz, $i) eq "0") {
    substr($charz, $i) = "L";
  } elsif (substr($charz, $i) eq "1") {
    substr($charz, $i) = "H";
  }
}

Now we get to a situation where the code runs, but nothing changes. That's because of that $i < @charz. The array is empty, so your loop doesn't run and nothing changes.

We can fix that by changing the foreach loop to this:

for (my $i=0; $i < length$charz; $i++) {
  ...
}

But that still doesn't work because of the substr issues that others have mentioned.

So what you were actually trying to write (I think) was this:

for (my $i=0; $i < length$charz; $i++) {
  if (substr($charz, $i, 1) eq "0") {
    substr($charz, $i, 1) = "L";
  } elsif (substr($charz, $i, 1) eq "1") {
    substr($charz, $i, 1) = "H";
  }
}

This works as expected. But you're doing far too much work here. You should have used tr/// :-)

Upvotes: 0

GeekyDeaks
GeekyDeaks

Reputation: 670

Try the following regexp:

$charz =~ s/0/L/g;
$charz =~ s/1/H/g;

Upvotes: 0

TLP
TLP

Reputation: 67890

Using tr// is probably the best solution, as has been noted. I just wanted to add this bitwise comparison method, since you are using bits, and its fun:

use strict;
use warnings;

my $bin = shift // 0x80;                 # take arg or default
my $str;
for (my $n = 1; $n <= $bin; $n *= 2) {   # $n -> 1, 2, 4, ...
    $str .= $bin & $n ? 'H' : 'L';       # check bits
}
$str = reverse $str;                     # well...
printf "Org: %b\nNew: $str\n", $bin;

Some test runs:

$ foo.pl
Org: 10000000
New: HLLLLLLL

$ foo.pl 3
Org: 11
New: HH

$ foo.pl 31
Org: 11111
New: HHHHH

$ foo.pl 33
Org: 100001
New: HLLLLH

Upvotes: 2

Jonathan Leffler
Jonathan Leffler

Reputation: 754130

Perl if statements always require { and } around the action:

if (substr($charz, $i) eq "0")
{
    substr($charz, $i) = "L";
}
else if (substr($charz, $i) eq "1")
{
    substr($charz, $i) = "H";
}

(You should probably look at the enclosing loop indexes too; arrays index from 0 in Perl unless you really like to live dangerously. And, while we're deconstructing the code, as TLP commented, the assignment to $charz via sprintf is a long-winded way of writing $charz = $binary_string;!)

Also, as hobbs points out in a comment, the substr() operator needs a third argument to work as you want it to:

if (substr($charz, $i, 1) eq "0")
{
    substr($charz, $i, 1) = "L";
}
else if (substr($charz, $i, 1) eq "1")
{
    substr($charz, $i, 1) = "H";
}

Or you could use:

substr($charz, $i, 1) = "L" if (substr($charz, $i, 1) eq "0");
substr($charz, $i, 1) = "H" if (substr($charz, $i, 1) eq "1");

Or you could avoid the explicit loop and use:

$charz =~ s/1/H/g;
$charz =~ s/0/L/g;

Or you could use:

$charz =~ y/01/LH/;

Or you could spell y as tr:

$charz =~ tr/01/LH/;

Or all the other ways you can do it in Perl...

Of the mechanisms shown, I'd use y or tr (probably tr is preferred; y is for die-hard sed programmers); it does it all in one line: no fuss, no muss.

Upvotes: 3

Miller
Miller

Reputation: 35208

Just use tr:

use warnings;
use strict;

my $i2c = "80";

my $binary_string = sprintf "%08b", hex($i2c);
print "$binary_string \n";

$binary_string =~ tr/10/HL/;
print "$binary_string\n";

Outputs:

10000000
HLLLLLLL

Upvotes: 4

Related Questions