I have a string of binary text which is a multiple of 8 characters long. Take the following for example.
$fullNameBin = "01010001000010110110010000010011000110000111100011001011111110110100111100111100";
I wish to convert this to hex. Note that the bits of each byte are in least significant to most significant order, so the above should result in
$fullNameCoded = "8AD026C8181ED3DFF23C";
In Perl, this can be achieved using
my $fullNameCoded = "";
for ( unpack( '(A8)*', $fullNameBin ) ) {
$fullNameCoded .= sprintf( "%02X", oct( "0b" . reverse( $_ ) ) );
}
or
my $fullNameCoded = uc unpack 'H*', pack 'b*', $fullNameBin;
PHP's pack/unpack is much more limited than Perl's, and a naive translation was unfruitful.
foreach ( unpack( "A8*", $fullNameBin) as $item ) {
$fullNameCoded .= sprintf( "%02X", octdec( "0b" . strrev( $item ) ) );
}
What I did is splitting the binary string representation into 8 chars, reversed it and created the hex representation from it and uppercased them.
$fullNameBin = "01010001000010110110010000010011000110000111100011001011111110110100111100111100";
$fullNameCoded = '';
foreach(str_split($fullNameBin, 8) as $char) {
$fullNameCoded .= strtoupper(dechex(bindec(strrev($char))));
}
gives
8AD026C8181ED3DFF23C
I may be totally wrong, please anyone correct me:
For me it looks like the unpack "A8*" can get 8 characters from the string, but is not capable of repeating until end when a number is given.
"A*" will give the whole string.
"A8" will give 8 chars
"A8*" will give also only 8 and does not repeat.
Conclusion
I think it does not work with unpack, because $fullNameBin is not binary, but a string representation of the binary data.
Related
I know this is a pretty silly question, but I don't know what to do.
I have an arbitrary binary number, say,
1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111
I want to convert it to Base 64 using PHP - and every way I try gives me a different result. Even different online converters convert it differently:
http://home2.paulschou.net/tools/xlate/
http://convertxy.com/index.php/numberbases/
PHP's base_convert only works up to base36, and base64_encode expects a string.
What do I do?
UPDATE: I implemented the solution functions suggested by #binaryLV, and it did work well.
However, I compared the results to PHP's built-in base_convert. It turned out that base_convert to base36 returns shorter values that the custom base64 function! (And yes, I did prepend a '1' to all the binary numbers to ensure leading zeros aren't lost).
I have noticed, too, that base_convert is quite innacurate with large numbers. So I need is a function which works like base_convert, but accurately and, preferably, up to base 64.
Length of a string in example is 160. It makes me think that it holds info about 160/8 characters. So,
split string into parts, each part holds 8 binary digits and describes single character
convert each part into a decimal integer
build a string from characters, that are made from ASCII codes from 2nd step
This will work with strings with size n*8. For other strings (e.g., 12 binary digits) it will give unexpected results.
Code:
function bin2base64($bin) {
$arr = str_split($bin, 8);
$str = '';
foreach ( $arr as $binNumber ) {
$str .= chr(bindec($binNumber));
}
return base64_encode($str);
}
$bin = '1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111';
echo bin2base64($bin);
Result:
kDICQIMBEFjDBxwMBJiAASBYwgc=
Here's also function for decoding it back to string of binary digits:
function base64bin($str) {
$result = '';
$str = base64_decode($str);
$len = strlen($str);
for ( $n = 0; $n < $len; $n++ ) {
$result .= str_pad(decbin(ord($str[$n])), 8, '0', STR_PAD_LEFT);
}
return $result;
}
var_dump(base64bin(bin2base64($bin)) === $bin);
Result:
boolean true
PHP has a built in base 64 encoding function, see documentation here. If you want the decimal value of the binary string first use bin2dec, there are similar functions for hexadecimals by the way. The documentation is your friend here.
[EDIT]
I might have misunderstood your question, if you want to convert between actual bases (base 2 and 64) use base_convert
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
echo base64_encode ($number);
This is if you want the exact string be converted into Base 64.
To convert a binary number (2 base) to a 64 base use the base_convert function.
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
base_convert ($number , 2, 64);
What does the "B" do in this pack statement from Perl code?
$hce_hash=pack('B*', $hce_hash);
Is there an equivalent function in PHP?
PHP’s pack doesn’t support a format of B*, but it does support H*. In Perl, you could emulate it with
sub pack_Bstar {
my($bits) = #_;
my $Hstar;
my $nybble = 0;
for (my $i = 0; $i < length $bits; ++$i) {
$nybble *= 2;
$nybble += int substr($bits, $i, 1);
if ($i % 4 == 3) {
$Hstar .= sprintf "%x", $nybble;
$nybble = 0;
}
}
my $pad = 4 - length($bits) % 4;
if ($pad != 4) {
$nybble = ($nybble << $pad);
$Hstar .= sprintf "%x", $nybble;
}
pack "H*", $Hstar;
}
The code above is not idiomatic Perl, but translation to PHP should be straightforward.
The H* format wants a hex string with high nybble (4 bits) first. The code above chews off four bits at a time to compute each nybble value. For example, for a bit string of 1011, tracing the algorithm gives
nybble = 0
nybble = 2 * 0 + 1 = 1
nybble = 2 * 1 + 0 = 2
nybble = 2 * 2 + 1 = 5
nybble = 2 * 5 + 1 = 11
10112 is indeed 1110, which is b16. If the last nybble is incomplete (between one and three bits), we left-shift the bit the appropriate number of places. This has the effect of zero-padding on the right.
Tests:
my #tests = (
["01001010011101010111001101110100" => "Just"],
["0110000101101110011011110111010001101000011001010111001" => "another"],
["01010000010010000101000000101111010100000110010101110010011011" => "PHP/Perl"],
["01101000011000010110001101101011011001010111001000101100" => "hacker,"],
);
for (#tests) {
my($input,$expect) = #$_;
my $got = pack_Bstar $input;
print "$input: ", ($got eq $expect ? "PASS" : "FAIL"), " ($got)\n";
}
Output:
01001010011101010111001101110100: PASS (Just)
0110000101101110011011110111010001101000011001010111001: PASS (another)
01010000010010000101000000101111010100000110010101110010011011: PASS (PHP/Perl)
01101000011000010110001101101011011001010111001000101100: PASS (hacker,)
pack 'B*', $s returns the bytes represented by the string of 0 and 1 characters that form up the string in $s. The value of $s is right-padded with zeros to a length divisible by 8 if necessary.
For example,
pack 'B*', '0100101000110101'
results in
chr(0b01001010) . chr(0b00110101);
As others have noted, PHP's pack() does not support the B template, which in Perl's pack() turns a bitstring, represented as a literal string of 0 and 1 characters, into a packed byte string with 8 bits per byte.
However, since PHP's pack() does support the H template, which does the same except for hexadecimal digits instead of bits, we can emulate Perl's B template in PHP by first using base_convert() to turn the bits into hex digits and then packing those:
function pack_B( $bits, $len = false ) {
// truncate input to desired length, if given:
if ( $len === false ) $len = strlen( $bits );
else $bits = substr( $bits, 0, $len );
// pad input with zeros to next multiple of 4 above $len:
$hexlen = (int)( ($len + 3) / 4 );
$bits = str_pad( $bits, 4*$hexlen, "0" );
// split input into chunks of 4 bits, convert each to hex and pack them:
$nibbles = str_split( $bits, 4 );
foreach ( $nibbles as $i => $nibble ) {
$nibbles[$i] = base_convert( $nibble, 2, 16 );
}
return pack( "H*", implode( "", $nibbles ) );
}
(The reason we can't just feed the whole input string to base_convert() is that it stores its intermediate result as a PHP float, and thus doesn't produce correct results for numbers too large to be accurately represented by a float. Doing it one hex digit at a time works fine, however.)
I'm trying to encode a string using the Crockford Base32 Algorithm.
Unfortunately, my current code only accepts numeric values as input. I thought of converting the ASCII characters to Decimal or Octal, but then the concatenation of 010 and 100 results in 10100 which makes it impossible to decode this. Is there some way to do this I am not aware of?
I believe this should be a more efficient implementation of Crockford Base32 encoding:
function crockford_encode( $base10 ) {
return strtr( base_convert( $base10, 10, 32 ),
"abcdefghijklmnopqrstuv",
"ABCDEFGHJKMNPQRSTVWXYZ" );
}
function crockford_decode( $base32 ) {
$base32 = strtr( strtoupper( $base32 ),
"ABCDEFGHJKMNPQRSTVWXYZILO",
"abcdefghijklmnopqrstuv110" );
return base_convert( $base32, 32, 10 );
}
(demo on codepad.org)
Note that, due to known limitations (or, arguably, bugs) in PHP's base_convert() function, these functions will only return correct results for values that can be accurately represented by PHP's internal numeric type (probably double). We can hope that this will be fixed in some future PHP version, but in the mean time, you could always use this drop-in replacement for base_convert().
Edit: The easiest way to compute the optional check digit is probably simply like this:
function crockford_check( $base10 ) {
return substr( "0123456789ABCDEFGHJKMNPQRSTVWXYZ*~$=U", $base10 % 37, 1 );
}
or, for large numbers:
function crockford_check( $base10 ) {
return substr( "0123456789ABCDEFGHJKMNPQRSTVWXYZ*~$=U", bcmod( $base10, 37 ), 1 );
}
We can then use it like this:
function crockford_encode_check( $base10 ) {
return crockford_encode( $base10 ) . crockford_check( $base10 );
}
function crockford_decode_check( $base32 ) {
$base10 = crockford_decode( substr( $base32, 0, -1 ) );
if ( strtoupper( substr( $base32, -1 ) ) != crockford_check( $base10 ) ) {
return null; // wrong checksum
}
return $base10;
}
(demo on codepad.org)
Note: (July 18, 2014) The original version of the code above had a bug in the Crockford alphabet strings, such that they read ...WZYZ instead of ...WXYZ, causing some numbers to be encoded and decoded incorrectly. This bug has now been fixed, and the codepad.org versions now include a basic self-test routine to verify this. Thanks to James Firth for spotting the bug and fixing it.
I know this is a pretty silly question, but I don't know what to do.
I have an arbitrary binary number, say,
1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111
I want to convert it to Base 64 using PHP - and every way I try gives me a different result. Even different online converters convert it differently:
http://home2.paulschou.net/tools/xlate/
http://convertxy.com/index.php/numberbases/
PHP's base_convert only works up to base36, and base64_encode expects a string.
What do I do?
UPDATE: I implemented the solution functions suggested by #binaryLV, and it did work well.
However, I compared the results to PHP's built-in base_convert. It turned out that base_convert to base36 returns shorter values that the custom base64 function! (And yes, I did prepend a '1' to all the binary numbers to ensure leading zeros aren't lost).
I have noticed, too, that base_convert is quite innacurate with large numbers. So I need is a function which works like base_convert, but accurately and, preferably, up to base 64.
Length of a string in example is 160. It makes me think that it holds info about 160/8 characters. So,
split string into parts, each part holds 8 binary digits and describes single character
convert each part into a decimal integer
build a string from characters, that are made from ASCII codes from 2nd step
This will work with strings with size n*8. For other strings (e.g., 12 binary digits) it will give unexpected results.
Code:
function bin2base64($bin) {
$arr = str_split($bin, 8);
$str = '';
foreach ( $arr as $binNumber ) {
$str .= chr(bindec($binNumber));
}
return base64_encode($str);
}
$bin = '1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111';
echo bin2base64($bin);
Result:
kDICQIMBEFjDBxwMBJiAASBYwgc=
Here's also function for decoding it back to string of binary digits:
function base64bin($str) {
$result = '';
$str = base64_decode($str);
$len = strlen($str);
for ( $n = 0; $n < $len; $n++ ) {
$result .= str_pad(decbin(ord($str[$n])), 8, '0', STR_PAD_LEFT);
}
return $result;
}
var_dump(base64bin(bin2base64($bin)) === $bin);
Result:
boolean true
PHP has a built in base 64 encoding function, see documentation here. If you want the decimal value of the binary string first use bin2dec, there are similar functions for hexadecimals by the way. The documentation is your friend here.
[EDIT]
I might have misunderstood your question, if you want to convert between actual bases (base 2 and 64) use base_convert
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
echo base64_encode ($number);
This is if you want the exact string be converted into Base 64.
To convert a binary number (2 base) to a 64 base use the base_convert function.
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
base_convert ($number , 2, 64);
I'm pretty inexperienced in PHP so this may be obvious to some of you, but if I call md5($mystring,true) in PHP it says it returns a "raw binary format with a length of 16". So what is that? Is it an array? An array of what? How do I read the individual bits and bytes of that return value?
None of the examples I can find online use it without going straight into base64_encode() or something. I just want to be able to check the fifth bit, or the third byte, for example.
var_dump(md5("string", TRUE));
"Raw binary format" means a string (as strings are binary-safe in PHP):
string(16) "�\����= �(��^{!"
If you want to read a byte out of that, use the $string[5] offset syntax for that. Or to extract a single bit out of the fifth byte for example:
$bit4_from_5th_byte = ord($result[5]) & (1 << 4);
It returns it as a "string" with each Character being a byte of the output. What you might consider instead is using the hex output of md5() without the second parameter and converting substrings of it to numbers using intval() with 16 as the base parameter. Like so:
$hash = md5($raw);
$byte = intval(substr($hash, 0, 2), 16);
An MD5 hash is a 128bit number, e.g. 16 bytes of raw binary data. For legibility, PHP's md5() function defaults to outputting this as a human readable string, where those 16 binary bytes get converted into a 32 character alpha-numeric string.
When you specify that second true parameter for MD5, you're telling PHP to return that raw 128bit number instead of the human readable version.
The RAW output of MD5 is the plain binary string of the hash. You cannot print it to the screen since it's not encoded as actual ASCII characters but binary numbers. This is only useful if you need to store or transfer the hash in a binary format.
To get the individual bits:
$md5 = md5( "b", true );
$l = strlen( $md5 );
$bits = "";
for( $i = 0; $i < $l; ++$i ) {
$num = ord( $md5[$i] );
for( $j = 7; $j >= 0; --$j ) {
$bits .= ( $num & ( 1 << $j ) ) ? "1" : "0";
}
}
echo $bits."\n";
//10010010111010110101111111111110111001101010111000101111111011000011101011010111000111000111011101110101001100010101011110001111
echo asciibin2hex( $bits ) == md5("b"); // true
So we go byte by byte, and since it is in string form, we need to convert it to ASCII number by ord(). Now go through the byte, bit by bit and see which are turned on and which are turned off and concatenate to the bit string. Go to next byte and rinse and repeat until all 128 bits are read.
Read third bit ( from the left ) in third byte:
ord( $md5[2] ) & ( 1 << ( 8 - 3 ) )
returns 1 if the bit is turned on, 0 otherwise