最可靠方法是unpack('c*', $str),直接获取字符串每个字节的0–255十进制值,正确处理utf-8多字节字符,避免误用bindec、base_convert或ord循环等常见错误。

PHP里用 unpack() 转字符串为二进制字节最可靠
直接用 unpack('C*', $str) 拿到每个字符的 ASCII/UTF-8 字节值,这是最贴近“字符串转二进制”的实际需求——你要的不是 Base64 或十六进制字符串,而是原始字节序列。
常见错误是误用 bindec() 或 base_convert(),它们只处理“看起来像二进制的字符串”(比如 "1010"),而不是把任意字符串拆成字节。
-
unpack('C*', $str)返回整数数组,每个元素是对应字节的 0–255 十进制值 - UTF-8 多字节字符会被正确拆开:比如中文
"中"(UTF-8 编码为\xe4\xb8\xad)会返回[228, 184, 173] - 如果只需要十六进制表示,用
unpack('H*', $str)更快,返回单个十六进制字符串(如"e4b8ad") - 别用
ord()循环处理——它只取第一个字节,对多字节 UTF-8 字符会截断
用 mb_convert_encoding() + unpack() 防止 UTF-8 乱码
直接对 UTF-8 字符串用 unpack('C*', $str) 是安全的,但前提是明确知道输入编码。如果来源不可控(比如用户 POST 数据、文件读入),先统一转成 UTF-8 再拆解,避免字节错位。
容易踩的坑:不检查编码就硬拆,导致一个汉字被当成多个非法字节,后续计算长度、校验、加密全出错。
立即学习“PHP免费学习笔记(深入)”;
- 先用
mb_detect_encoding($str, ['UTF-8', 'GB2312', 'GBK'], true)猜编码(注意第三个参数true表示 strict) - 再用
mb_convert_encoding($str, 'UTF-8', $detected)归一化 - 最后
unpack('C*', $normalized)—— 这一步才真正反映字符串的底层字节 - 如果确定全是 ASCII(如 token、ID),跳过检测,
unpack('C*', $str)直接用,更快
pack() 是 unpack() 的逆操作,别反着用
有人想“把二进制数组转回字符串”,结果写 pack('C*', [...]) 却得到乱码,问题往往出在字节数组本身就不对——比如把 UTF-8 字节当成了 Latin-1 去解,再 pack 回去当然错。
关键判断点:pack 出来的字符串,必须和 unpack 前的原始字符串 === 全等(不是 ==),否则中间有编码转换损失。
- 正确流程:
$bytes = unpack('C*', $str); $restored = pack('C*', ...$bytes); $restored === $str - 如果
$restored !== $str,说明原始$str含有非 UTF-8 字节,或之前被错误转码过 -
pack('H*')适合还原unpack('H*', $str)的结果,性能比C*高,但只适用于十六进制字符串场景
二进制操作别绕远路:不用 base64、hex、bindec
Base64 和十六进制只是编码格式,不是二进制本身。如果你要算 CRC、做 XOR 加密、对接硬件协议,必须拿到原始字节,而不是 base64_encode() 后的字符串。
典型误用:bindec(dechex(ord($str[0]))) —— 这连一个字节都没完整拿到,纯属白忙。
- 需要传输/存储?用
base64_encode($str),收端base64_decode(),中间不碰 unpack - 需要调试查看?用
bin2hex($str)(比unpack('H*', $str)更简洁) - 需要逐字节逻辑运算?坚持用
unpack('C*', $str),然后foreach处理整数数组 - 大文件别一次性
file_get_contents()+unpack,内存爆得快;改用fopen()+fread()分块处理
最常被忽略的一点:PHP 的字符串本身就是字节数组,unpack('C*', $str) 不是“转换”,只是显式暴露它——所以别在中间加任何编码转换,除非你清楚自己在修复什么。











