Perl: the Markov chain saw PerlMonks

### Convert ASCII string to 7-bit binary string

by Pascal666 (Scribe)
 on Oct 27, 2015 at 18:54 UTC Need Help??

Pascal666 has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to convert from an ASCII string to the 7-bit binary representation of that string. So "4B" converts to "01101001000010", for example. The below works, but I feel like there has to be a better way.

```for (split //, '4B') {
\$_ = unpack 'B8';
s/^.//;
\$out .= \$_;
}

Replies are listed 'Best First'.
Re: Convert ASCII string to 7-bit binary string
by kennethk (Abbot) on Oct 27, 2015 at 19:22 UTC
Define better. More readable/maintainable? Faster? Algorithmically more elegant? I'm always a huge fan of "works," which your code does.
```print my \$out = join '', map {substr unpack('B8'), 1} split //, '4B';
substr, unpack
```print my \$out = join '', map {sprintf '%07b', ord} split //, '4B';
sprintf, ord

#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

why is substr needed there?
substr \$var, 1 returns the string in \$var minus the first character. That's what converts the 8 character binary output from unpack into the desired 7 character one. You could do the same thing with a substitution with the /r modifier, and I'm sure a number of other methods. In the OP, it's handled with s/^.//;, but then you can't chain.

#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Convert ASCII string to 7-bit binary string
by AnomalousMonk (Bishop) on Oct 27, 2015 at 20:34 UTC

```c:\@Work\Perl\monks>perl -wMstrict -le
"my \$out;
;;
\$out .= reverse for unpack '(b7)*', '4B3A';
print qq{'\$out'};
"
'0110100100001001100111000001'

Give a man a fish:  <%-{-{-{-<

A slight variant that unpacks in the right order and drops the reverse for the expense of a second unpack inside a map.

```\$ perl -Mstrict -Mwarnings -E '
my \$out;
\$out .= \$_ for map { unpack q{xA7}, \$_ } unpack q{(B8)*}, q{4B3A};
say \$out;'
0110100100001001100111000001
\$

Not as succinct but possibly slightly easier to work out what is going on because it is not immediately clear that your b7 format will drop the last (most significant) bit before you reverse the bits into the correct order.

Cheers,

JohnGG

Inspired by kennethk's second approach above, this avoids the reversible complexity of either  '(B8)*' or  '(b7)*' in favor of  'C*' simplicity:

```c:\@Work\Perl\monks>perl -wMstrict -le
"my \$out;
\$out .= sprintf '%07b', \$_ for unpack 'C*', '4B3A';
print qq{'\$out'};
"
'0110100100001001100111000001'

Give a man a fish:  <%-{-{-{-<

why do you reverse?

Because the  b7 unpack template that drops the most significant bit of the converted character also produces the binary digits in little-endian order. The OPer wants them in big-endian (i.e., most significant binary digit on the left) order, hence the 7-character sub-string must be reversed before being appended to the  \$out string. Try it without reverse and see what you get.

Give a man a fish:  <%-{-{-{-<

Re: Convert ASCII string to 7-bit binary string
by ikegami (Pope) on Oct 28, 2015 at 21:18 UTC

For long input strings, the following should be faster than the previously submitted solutions:

```my \$out = unpack('B*', \$str) =~ s/.(.{7})/\$1/sgr;     # 5.14+

( my \$out = unpack('B*', \$str) ) =~ s/.(.{7})/\$1/sg;  # 5.6+
Re: Convert ASCII string to 7-bit binary string
by Anonymous Monk on Oct 27, 2015 at 19:25 UTC
```\$out = unpack('B*', '4B') =~ s/.(.{7})/\$1/gr;
Re: Convert ASCII string to 7-bit binary string
by AppleFritter (Vicar) on Oct 27, 2015 at 22:17 UTC

Here's another regex-based solution:

```\$string = '4B';

(\$out = \$string) =~ s/(.)/unpack 'B8', \$1/eg;

Fairly straightforward, actually - this globally (/g) matches every character ((.)), executes (/e) the embedded snippet of code, and replaces the matched character with the result of the call to unpack.

EDIT: please see below for a version that actually works properly.

Problem is, that doesn't actually work. That outputs "0011010001000010" instead of "01101001000010".

Mea culpa; I should pay more attention, and not just assume that two similar-looking binary strings are, in fact, the same. Thanks for the correction.

Fortunately it's easily-fixed; just use substr in the substitution to chop off the first character:

```(\$out = \$string) =~ s/(.)/substr unpack('B8', \$1), 1/eg;

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1146161]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2020-10-24 00:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
My favourite web site is:

Results (242 votes). Check out past polls.

Notices?