From: bianbian.sunshow.net/index.php/technology/php/144.html
最近才对编码问题真正深入研究。因为我碰到了这个问题:客户端传过来的是对UTF-16的base64编码,而php没有办法正确解码。
有关编码,可以先看看这篇文章:字符,字节和编码
关于UTF-8和UTF-16,可以参考:谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
有关URL的base64处理,可以参考这篇:通过 URL 传递 base64 编码参数的问题
摘录如下:
一般情况下,URL 中的参数应使用 url 编码规则,即把参数字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。但是对于带有中文的参数来说,这种编码会使编码后的字符串变得很长。如果希望有短一点的方式对参数编码,可以采用 base64 编码方式对字符串进行编码,但是 base64 编码方式不能处理 JavaScript 中的中文,因为 JavaScript 中的中文都是以 UTF-16 方式保存的。而 base64 只能处理单字节字符,所以不能直接用 base64 对带有中文的 JavaScript 字符串进行编码。但是可以通过 utf.js 这个程序中提供的 utf16to8 来将 UTF-16 编码的中文先转化为 UTF-8 方式,然后再进行 base64 编码。这样编码后的字符串,在传递到服务器端后可以直接通过 base64_decode 解码成 UTF-8 的中文字符串。但是还有个问题需要注意。base64 编码中使用了加号(+),而 + 在 URL 传递时会被当成空格,因此必须要将 base64 编码后的字符串中的加号替换成 %2B 才能当作 URL 参数进行传递。否则在服务器端解码后就会出错。
不过,我对上述的“而 base64 只能处理单字节字符”有疑问,这是base64的标准规定,还是php的缺陷?
如果是php的缺陷,我想应该能写一个针对UTF-16的base64解码函数。这样就不需要客户端来进行UTF16转成UTF8了。
补充:
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
上面第二篇摘录如下:
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
也就是说,base64编码必须针对单字节。从这个角度说,是不能对UTF-16的字串进行base64编码操作的。难怪php解码后得到的结果不一致。原来问题不是出在php上。
我又看了客户端base64编码部分的代码,确实是客户端的问题。客户端用了“(const BYTE*)sSrcBuf ”把UTF-16的双字节强制转为BYTE流之后进行base64编码,结果是错误的。难怪我解码以后英文字符之间都有莫名奇妙的“空格”(实际是“00”,但是记事本看网页源代码上的“00”显示成空格)。
实际上是这样的:比如“ABC”,UTF-8字节流是“41 42 43”。而UTF-16则是“41 00 42 00 43 00”,所以“(const BYTE*)sSrcBuf ”之后,“00”就被作为单独的一个字节进行了转码,所以解码以后就还原为UTF-8的字节流“41 00 42 00 43 00”,这时候跟初始的“41 42 43”已经不一致了(print的时候只能打出“A”)。难怪我的SQL会报出“ERROR: unterminated quoted string at or near “‘A” at character”的错,因为数据库读取SQL读到“00”就认为结束了。
这时候我冒出一个想法,要是服务器端解码以后再强制把字节流当做UTF-16转为UTF-8,结果不就一样了吗?
事实证明我的想法是正确的:
- $utf16 = chr(hexdec('FF')) . chr(hexdec('FE')) . base64_decode($_GET[abc]);
- print(iconv('UTF-16', 'UTF-8', $utf16));
- //输出"ABC",这是对的!
最后还有一个问题,UTF-16的BOM不一定是FF FE,也可能是FE FF。这怎么办?要是有ASCII字符还好办,两个两个的找,看“00”是在前还是在后。要是双字节都有内容呢?举上面文章的例子:“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
这个问题单纯改服务器端php没有办法解决。最终的解决办法只有两个:
1)如上面文章所说,客户端UTF-16先转成UTF-8再base64编码,再提交;显示的时候转回UTF-16
2)客户端可以基于UTF-16的字节流base64编码,但是提交的时候要带上“BOM”,即指明是“FFFE”,还是“FEFF”
当然,如果能确定客户端的Unicode的BOM方式,上面的2)也可以不用。
直接在服务器端加上BOM标记进行转码即可。
好像Windows下都是FFFE的,所以可以直接用这个:
- //加上UTF-16 BOM的base64解码(客户端是根据FFFE的Unicode base64的)
- function get_utf8_from_base64_utf16(&$str)
- {
- $utf16 = chr(hexdec('FF')) . chr(hexdec('FE')) . base64_decode($str);
- return iconv('UTF-16', 'UTF-8', $utf16);
- }
分享到:
相关推荐
易语言汇编base64编码源码,汇编base64编码,Base64Encode,Base64Decode
由于遇到做一个支付平台的接口参数要做base64 于是就写了一个用javascript编写的方法,对方支付平台是java的 ,于就将中文做了一个unicode转为assi码处理。
labview 图片缩放 base64编码base64解码
Hex十六进制/Base64编码转换器,可对字符串文本进行十六进制及Base64编码与反编码
标注base64编码方式 Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。 关于这个编码的规则...
需要一个二进制与BASE64相互转换的函数,从网上找...示例中提供二进制与16进制字符串转换函数ConvertHexStrToBin与ConvertBinToHexStr,二进制与BASE64编码转换函数Base64Encode与Base64Decode,在VC6下编译通过可用。
可以实现将文件转换为Base64编码。可以实现将文件转换为Base64编码。
base64 加解码 base64转hex Base64转图片 图片转Base64工具 可选Unicode UTF8等编码格式。前一个不 支持换行,此版本已支持。 (使用前,麻烦安装DotNet4.0以上版本)
实现BASE64编码和解码程序, 在类中实现如下函数并运行测试正确。 BASE64编码算法请在网上查询。 public String encode(byte[] data) { } public byte[] decode(String b) { }
base64 编码转换工具 base64加密解密工具
提供一种Base64编码,并输出UTF-8格式的BASE64编码方式。本程序在微信小程序开发工具中已经测试通过。 Base64代码: [javascript] view plain copy print? (function(){ var BASE64_MAPPING = [ 'A','B','C','D',...
* 2、base64unicode.js是unicode格式的加密与解密,支持更多的字符; *在java代码中的使用如下: * 1、加密:Base64.encodeToString(vo.getText().getBytes("unicode")) * 2、解密:String keywords =new String...
Base64编码与图片互转
2) 修复 BASE64编码解码,个别情况下不会选择AVX2模式的问题(C/C++的bool类型是单字节,易的逻辑型是4字节,易的逻辑型为真时 转为字节集可能是{0,0,1,0},导致单字节判断 时灵时不灵)。1.4更新(2019.6.5)。 1) 添加...
这是一个很好的Base64编-解码工具.转换很方便,支持中文,支持UTF-8,... 解码: 在下面的窗口输入base64的编码(最多可输入25000个字符,软件可以自动过滤掉非base64编码),同时即可在上面的窗口输出解码后的信息。
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论...
我在网上找了半天的java、js的base64编码解码结果没有,只好自己写了,js一个文件,java一个文件,调用里面是使用方法
base64不用考虑 编码的问题,充分解决客户端 和服务之间乱码的问题
1.Java生成二维码示例 2.图片保存到本地或生成Base64编码 3.Base64编码生成图片
android 开发中有时会需要用到base64加解密...google在android.util下提供了一个Base64工具类,可以很方便的用它encode和decode,里面提供了一些经典的常用算法。 此程序为改方法完整Java代码,可以直接运行或者调用。