使用PHP将字符串转换为二进制,然后再转换回来

70

在标准PHP库中,是否有将字符串转换为二进制,然后再转回字符串的方法?

为了澄清我的意图,我要把密码存储在数据库中。 我将首先使用哈希函数进行转换,然后最终以二进制形式存储。


我发现最好的方法是使用此函数。 似乎可以同时进行哈希和二进制输出。

http://php.net/manual/en/function.hash-hmac.php


你是在尝试编码/解码字符串吗?为什么要将字符串转换为二进制? - Arda
1
你想做什么,遇到了什么问题? - hakre
1
哈希与加密不同,哈希是一种单向过程。(您无法解密哈希,只能尝试匹配它)。 - Agi Hammerthief
11个回答

73
你想要使用packbase_convert
// Convert a string into binary
// Should output: 0101001101110100011000010110001101101011
$value = unpack('H*', "Stack");
echo base_convert($value[1], 16, 2);

// Convert binary into a string
// Should output: Stack
echo pack('H*', base_convert('0101001101110100011000010110001101101011', 2, 16));

strlen('101001101110100011000010110001101101011')== 39吗?这不应该是偶数吗? - therealszaka
2
你说得完全正确!它缺少了前导0,对此我很抱歉。 - Francois Deschenes
2
更糟糕的是,strlen(base_convert($value[1],16,2)) ==39 ! - therealszaka
为什么是39? - Jimmy Kane
2
我尝试使用你的代码将001106130720160454转换为二进制。但是它只给了我零。 - Sajitha Rathnayake
这个无法处理“0123456789”和结果二进制的'0011001000110011001101000011010100110110000000000000000000000000'。 - AKMorris

44

当然可以!

那么...

$bin = decbin(ord($char));

... 然后再返回来。

$char = chr(bindec($bin));

4
对于字符串拼接,也许可以添加 str_pad(..., 8, 0, STR_PAD_LEFT) :) - Yoshi
2
@Yoshi 感谢你完成我的回答... @cfarm54 当然,我上面说的只是针对单个字符的情况! - SteeveDroz
@Yoshi,你能否给一个完整的示例来展示如何完成这个任务? - RSM
只有当初始二进制字符串的长度可被8整除时才有效。 - paullb

11

一个字符串只是一系列字节的序列,因此在PHP中它实际上是二进制数据。您到底想做什么?

编辑

如果您想在数据库中存储二进制数据,问题通常是数据库中的列定义。PHP不区分二进制数据和字符串,但数据库会。例如,在MySQL中,您应该将二进制数据存储在 BINARY, VARBINARYBLOB 列中。

另一种选择是使用base64_encode对PHP字符串进行编码,并将其存储在数据库中的某个 VARCHARTEXT 列中。但请注意,当使用base64_encode时,字符串的长度将增加。


是的,我的数据库有一个使用“BINARY”类型定义的列,但如果我要将一个变量(密码)传递给数据库,它也应该是二进制类型。我想知道如何进行这种转换。 - locoboy
你是如何将数据传递到数据库中的(使用的函数)?你使用哪个数据库? - Stefan Gehrig

7
您的哈希已经是二进制格式,可以直接用于数据库。

但是,您需要将其转换为数据库列定义所期望的格式。

在 PHP 中,任何字符串(直到 5.3 及以后版本)都是二进制字符串。这意味着它只包含二进制数据。

(这没有改变,PHP 6 没有实现,本答案的其余部分可能仅具有历史意义,它仍然可以工作,但我IRC中的 b'string' 已被删除 - 或者没有。)

但是,由于向后兼容 PHP 6,您可以将字符串显式转换为二进制:

$string = 'my binary string';
$binary = b'my binary string';

但那只是为了兼容性而已,在您的代码中,您可以直接这样做:
$string = $binary; // "convert" binary string into string
$binary = $string  // "convert" string into binary string

因为它是相同的。"convert"是多余的。

如果我将数据以二进制格式存储在数据库中,如何在 php 中指定 where 条件(而不将数据库字段转换为字符串)。即 WHERE field = binary_value_from_php?(当然,在 php 中,用户输入的是字符串)。PHP 中是否有 string_to_mysql_binary 函数? - Andrew
@Andrew:PHP中的字符串是二进制的。mysql驱动程序将执行(非)转换,即按照您告诉mysql列是二进制字符串的方式保持数据不变。更清楚了吗? - hakre
但它不起作用。当我将单词“car”存储在数据库中时,它不会被存储为“car”(字面意思),因此当我想要查找此记录时,我无法使用where字段=“car”,因为它无法找到... - Andrew
放心,它完美地工作。也许只是对这些命令的理解和数据库中字符串编码实际含义的问题?你所提到的搜索并不意味着你正在寻找二进制编码,通常你会使用它来编码数据库不支持的内容,然后你不会使用数据库来搜索它。只是说一下。 - hakre
是的,你说得对,它可以自动工作,一切都没问题。我在其他地方遇到了问题,可能是MySQL的错误或其他设置问题,所以很抱歉造成了混淆。谢谢。 - Andrew

5

我发现最简单的方法是将其转换为十六进制而不是字符串。如果这对您有用:

$hex = bin2hex($bin); // It will convert a binary data to its hex representation

$bin = pack("H*" , $hex); // It will convert a hex to binary

或者

$bin = hex2bin($hex); // Available only on PHP 5.4

2
我强烈建议使用PHP内置的标准密码库(ref.password.php)。这里有一个很好的示例,可以告诉你如何使用它们。
如果您要将二进制字符串转换为十进制数/字符,可以像下面这样进行操作...
echo bindec("00000001") . "\n";
echo bindec("00000010") . "\n";
echo bindec("00000100") . "\n";
echo bindec("00001000") . "\n";
echo bindec("00010000") . "\n";
echo bindec("00100000") . "\n";
echo bindec("01000000") . "\n";
echo bindec("10000000") . "\n";
echo bindec("01000001") . "\n";

# big binary string
echo bindec("111010110111011110000110001")."\n";

上述输出:
1
2
4
8
16
32
64
128
65
123452465

要将十进制数转换为字符/字符串,您可以这样做:
# convert to binary strings "00000001"
echo decbin(1) . "\n";
echo decbin(2) . "\n";
echo decbin(4) . "\n";
echo decbin(8) . "\n";
echo decbin(16) . "\n";
echo decbin(32) . "\n";
echo decbin(64) . "\n";
echo decbin(128) . "\n";

# convert a ascii character
echo str_pad(decbin(65), 8, 0, STR_PAD_LEFT) ."\n";

# convert a 'char'
echo str_pad(decbin(ord('A')), 8, 0, STR_PAD_LEFT) ."\n";

# big number...
echo str_pad(decbin(65535), 8, 0, STR_PAD_LEFT) ."\n";
echo str_pad(decbin(123452465), 8, 0, STR_PAD_LEFT) ."\n";

上述输出:

1
10
100
1000
10000
100000
1000000
10000000
01000001
01000001
1111111111111111
111010110111011110000110001

1

在2021年,任何人都可以使用SteeveDroz答案;但不幸的是,这仅适用于一个字符。因此,我将其放入for循环中,循环遍历并更改字符串的每个字符。

编辑:我刚刚意识到,我制作的binary_encode函数没有将字符转换为8位(这非常重要),它将它们转换为6-7位,但幸运的是,我只需要在前面添加所需的额外0即可使其成为8位。我更新了下面的编码函数。 另外,我不需要修复解码函数,因为它可以使用前置的0来工作,也可以不使用 :)

函数(已更新):

function binary_encode($str){
    
    # Declare both Binary variable and Prepend variable
    $bin = (string)""; $prep = (string)"";
    
    # Iterate through each character of our input ($str) 
    for($i = 0; $i < strlen($str); $i++){
        
        # Encode The current character into binary
        $bincur = decbin( ord( $str[$i] ) );
        
        # Count the length of said binary
        $binlen = strlen( $bincur );
        
        # If the length of our character in binary is less than a byte (8 bits); Then
        # For how ever many characters it is short;
        # it will replace with 0's in our Prepend variable.
        if( $binlen < 8 ) for( $j = 8; $j > $binlen; $binlen++ ) $prep .= "0"; 
        
        # Build our correct 8 bit string and add it to our Binary variable
        $bin .= $prep.$bincur." ";
        
        # Clear our Prepend variable before the next Loop
        $prep = "";

    }

    # Return the final result minus the one whitespace at the end
    # (from our for loop where we build the 8 bit string
    return substr($bin, 0, strlen($bin) - 1);

}

function binary_decode($bin){
    $char = explode(' ', $bin);
    $nstr = '';
    foreach($char as $ch) $nstr .= chr(bindec($ch));
    return $nstr;
}

使用方法:

$bin = binary_encode("String Here");
$str = binary_decode("1010011 1110100 1110010 1101001 1101110 1100111 100000 1001000 1100101 1110010 1100101");

旧版演示:

http://sandbox.onlinephpfunctions.com/code/2553fc9e26c5148fddbb3486091d119aa59ae464

新的实时演示:

http://sandbox.onlinephpfunctions.com/code/1d71888cd41371646431f9914ccd86cf5ef6303e


1
在PHP中,字符串始终是BLOB。因此,您可以使用字符串来保存数据库BLOB的值。所有这些基本转换等操作都与呈现该BLOB有关。
如果要获得漂亮的人类可读表示形式的BLOB,则显示其包含的字节并可能使用十六进制而非十进制是有意义的。因此,字符串“41 42 43”是一种很好的呈现在C#中将是的字节数组。
var bytes = new byte[] { 0x41, 0x42, 0x43 };

但是,显然这不是一种好的方式来表示这些字节!字符串"ABC"是一个高效的表示方法,因为实际上它是相同的BLOB(只是在这种情况下它不是那么大)。

在实践中,您通常会从返回字符串的函数中获取BLOBs - 比如哈希函数或其他内置函数,如fread

在罕见的情况下(但在尝试/原型时并不罕见),您需要从一些硬编码的字节构造字符串,我不知道比将“十六进制字符串”转换为PHP中通常称为“二进制字符串”的任何东西更有效:

$myBytes = "414243";
$data = pack('H*', $myBytes);

如果您执行var_dump($data);,它将显示string(3) "ABC"。这是因为0x41 = 65十进制= 'A'(在基本上所有编码中)。
由于将二进制数据解释为字符串并不直观,您可能希望创建一个基本包装器以使调试更加容易。一个可能的包装器如下:
class blob
{
    function __construct($hexStr = '')
    {
        $this->appendHex($hexStr);
    }

    public $value;

    public function appendHex($hexStr)
    {
        $this->value .= pack('H*', $hexStr);
    }

    public function getByte($index)
    {
        return unpack('C', $this->value{$index})[1];
    }

    public function setByte($index, $value)
    {
        $this->value{$index} = pack('C', $value);
    }

    public function toArray()
    {
        return unpack('C*', $this->value);
    }
}

这是我临时想出来的东西,可能只是你自己包装器的起点。但是,使用字符串作为存储方式是PHP中可用的最有效结构,同时提供像toArray()这样的方法,以便在调试器观察/评估时使用,当您想要检查内容时。
当然,您也可以使用完全直接的PHP数组,并在与使用字符串进行二进制数据交互时将其打包为字符串。根据您实际修改blob的程度,这可能会更容易,虽然它不是空间效率高,但我认为对于许多任务,您会获得可接受的性能。
下面是一个示例,以说明功能:
// Construct a blob with 3 bytes: 0x41 0x42 0x43.
$b = new blob("414243");

// Append 3 more bytes: 0x44 0x45 0x46.
$b->appendHex("444546");

// Change the second byte to 0x41 (so we now have 0x41 0x41 0x43 0x44 0x45 0x46).
$b->setByte(1, 0x41); // or, equivalently, setByte(1, 65)

// Dump the first byte.
var_dump($b->getByte(0));

// Verify the result. The string "AACDEF", because it's only ASCII characters, will have the same binary representation in basically any encoding.
$ok = $b->value == "AACDEF";

嗯,我正在学习这些东西,并且刚刚发现你实际上可以使用转义序列 \x 直接指定字符串的二进制内容。因此,$s = "\x41" 等同于 $s = "A"(前提是源代码存储在 A 是字节 0x41 的编码中)。 - Dojo

0

很有趣,Stefan Gehrig的答案实际上是正确的。您不需要将字符串转换为“011010101”字符串才能将其存储在数据库的BINARY字段中。无论如何,既然这是谷歌搜索“php convert string to binary string”时出现的第一个答案,那么我对此问题做出了贡献。

Francois Deschenes得到的最高票答案对于长字符串(字节串或位串)是错误的,因为

由于与内部使用的“double”或“float”类型相关的属性,base_convert()可能会在大数字上失去精度。有关更具体的信息和限制,请参见手册中的浮点数部分。

来自:https://secure.php.net/manual/en/function.base-convert.php

为了解决这个限制,您可以将输入字符串切成块。下面的函数实现了这种技术。

<?php

function bytesToBits(string $bytestring) {
  if ($bytestring === '') return '';

  $bitstring = '';
  foreach (str_split($bytestring, 4) as $chunk) {
    $bitstring .= str_pad(base_convert(unpack('H*', $chunk)[1], 16, 2), strlen($chunk) * 8, '0', STR_PAD_LEFT);
  }

  return $bitstring;
}

function bitsToBytes(string $bitstring) {
  if ($bitstring === '') return '';

  // We want all bits to be right-aligned
  $bitstring_len = strlen($bitstring);
  if ($bitstring_len % 8 > 0) {
    $bitstring = str_pad($bitstring, intdiv($bitstring_len + 8, 8) * 8, '0', STR_PAD_LEFT);
  }

  $bytestring = '';
  foreach (str_split($bitstring, 32) as $chunk) {
    $bytestring .= pack('H*', str_pad(base_convert($chunk, 2, 16), strlen($chunk) / 4, '0', STR_PAD_LEFT));
  }

  return $bytestring;
}

for ($i = 0; $i < 10000; $i++) {
  $bytestring_in = substr(hash('sha512', uniqid('', true)), 0, rand(0, 128));
  $bits = bytesToBits($bytestring_in);
  $bytestring_out = bitsToBytes($bits);
  if ($bytestring_in !== $bytestring_out) {
    printf("IN  : %s\n", $bytestring_in);
    printf("BITS: %s\n", $bits);
    printf("OUT : %s\n", $bytestring_out);
    var_dump($bytestring_in, $bytestring_out); // printf() doesn't show some characters ..
    die('Error in functions [1].');
  }
}


for ($i = 0; $i < 10000; $i++) {
  $len = rand(0, 128);
  $bitstring_in = '';
  for ($j = 0; $j <= $len; $j++) {
    $bitstring_in .= (string) rand(0,1);
  }
  $bytes = bitsToBytes($bitstring_in);
  $bitstring_out = bytesToBits($bytes);

  // since converting to byte we always have a multitude of 4, so we need to correct the bitstring_in to compare ..
  $bitstring_in_old = $bitstring_in;
  $bitstring_in_len = strlen($bitstring_in);
  if ($bitstring_in_len % 8 > 0) {
    $bitstring_in = str_pad($bitstring_in, intdiv($bitstring_in_len + 8, 8) * 8, '0', STR_PAD_LEFT);
  }

  if ($bitstring_in !== $bitstring_out) {
    printf("IN1  : %s\n", $bitstring_in_old);
    printf("IN2  : %s\n", $bitstring_in);
    printf("BYTES: %s\n", $bytes);
    printf("OUT  : %s\n", $bitstring_out);
    var_dump($bytes); // printf() doesn't show some characters ..
    die('Error in functions [2].');
  }
}

echo 'All ok!' . PHP_EOL;

请注意,如果您插入的位字符串不是8的倍数(例如:"101"),在转换为字节字符串时将无法恢复原始位字符串。从字节字符串转换回来时,您将得到"00000101",它在数值上相同(无符号8位整数),但字符串长度不同。因此,如果位字符串的长度对您很重要,您应该将长度保存在一个单独的变量中,并在转换后切掉字符串的开头部分。
$bits_in = "101";
$bits_in_len = strlen($bits_in); // <-- keep track if input length
$bits_out = bytesToBits(bitsToBytes("101"));
var_dump($bits_in, $bits_out, substr($bits_out, - $bits_in_len)); // recover original length with substr

0
我在寻找一些字符串位转换的内容,然后来到了这里, 如果下一个案例适用于您,请采用//它... 如果您想将字符串中的位用于不同的位, 也许这个例子会有所帮助。
$string="1001"; //this would be 2^0*1+....0...+2^3*1=1+8=9
$bit4=$string[0];//1
$bit3=$string[1];
$bit2=$string[2];
$bit1=$string[3];//1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接