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);
- }
分享到:
相关推荐
在这个特定的场景中,我们关注的是如何在LabVIEW中处理图片,包括图片的缩放、Base64编码和解码。 1. **图片缩放**: 在LabVIEW中,你可以使用图像处理库来实现图片的缩放功能。这个库提供了各种图像操作函数,...
* 2、base64unicode.js是unicode格式的加密与解密,支持更多的字符; *在java代码中的使用如下: * 1、加密:Base64.encodeToString(vo.getText().getBytes("unicode")) * 2、解密:String keywords =new String...
Base64编码的基本原理是将每3个8位字节(24位)的数据转换成4个6位的十六进制数字,每个6位对应一个字符,这些字符来自于Base64的字符集,包括大小写字母、数字以及"+"和"/",末尾不足四位的数据用"="进行填充。...
这个Demo的价值在于其代码简洁明了,方便其他开发者理解和复用,解决在QT项目中涉及图片Base64编码和解码的问题。通过学习和理解这个示例,开发者可以快速掌握在QT环境下处理图片Base64编码的技巧,提高代码的效率...
"Base64编码解码工具V1.5"是一个专为此目的设计的软件,它能方便地对Base64字符串进行编码和解码,并且能有效处理可能出现的乱码问题。该软件的.exe文件表明它是一个独立的应用程序,可以直接在Windows系统上运行。...
Base64编码是一种在网络上传输二进制数据的文本编码方式,主要应用于电子邮件、网页编码等领域,确保数据在传输过程中的安全性和可读性。它将任意二进制数据转换成可打印的ASCII字符,避免了非ASCII字符在传输过程中...
在Java和JavaScript中,Base64编码和解码是两个重要的功能,用于处理这种编码格式。下面将详细阐述这两个编程语言中实现Base64编码和解码的方法。 首先,让我们关注Java。在Java 8及更高版本中,已经内置了`java....
Base64编码是一种在互联网上广泛...总的来说,Base64编码解码器是一个实用的工具,尤其对于那些需要处理Base64编码数据的IT从业者或开发者来说,它可以方便地进行数据的转换,确保数据在各种环境下的正确传输和存储。
总的来说,这个"base64编码解码离线工具"为用户提供了一个方便、实用的平台,可以在本地快速处理Base64编码和解码任务,尤其适合处理包含换行符的数据。用户可以根据自己的需求,结合这个工具来提高工作或学习的效率...
这个压缩包中的"Base64"文件应该是一个工具程序,用户无需安装,解压后直接运行即可对Base64文本进行编码和解码操作。这样的工具对于开发者、网络管理员或者需要处理Base64数据的用户非常方便,他们可以直接通过这个...
解码库需要正确识别和处理编码字符串中的'='字符,以及可能出现的非Base64字符,确保解码的准确性。 在这个C语言实现的Base64库中,很可能包含了两个主要函数:一个用于编码,一个用于解码。编码函数可能接受一个二...
5. **文件操作**:除了处理字节数组,你还可以直接对文件进行Base64编码和解码。例如,要编码一个文件: ```java File inputFile = new File("path_to_input_file"); byte[] fileBytes = Files.readAllBytes...
在Java中,处理Base64编码和解码的操作通常涉及到网络通信、数据序列化以及安全加密等方面。下面我们将详细探讨Java中的Base64编码和解码原理及其实现方式。 Base64编码的基本思想是将每3个字节(24位)的数据转换...
在编程中,许多编程语言提供了内置的库或者函数来处理Base64编码和解码。例如,在Python中,可以使用`base64`模块的`b64encode()`和`b64decode()`函数来实现文件的Base64编码和解码。其他语言如Java、JavaScript、C#...
需要注意的是,由于Base64编码有特定的字符集和结束标识,解码过程中需正确处理这些特殊字符。 3. **转换为二进制数据**:解码后的字符串是6位一组的二进制数据表示,需要将其组合成原始的二进制数据。 4. **创建...
- 这4个6位块分别对应一个字符,这些字符来自BASE64的字符集:'A'到'Z','a'到'z','0'到'9',以及'+'和'/'。其中第64个字符通常是'=',用于表示原始数据不足3字节时的填充。 - 由于6位可以表示64种不同的值,因此...
在后端,我们可以使用Java的`java.util.Base64`类进行Base64编码和解码。以下是一个简单的Servlet示例,用于接收前端发送的Base64编码字符串并解码: ```java import javax.servlet.http.*; import java.io.*; ...
这个LabVIEW实现的Base64编码VI为开发者提供了一种在纯LabVIEW环境下处理Base64编码问题的方法,避免了引入第三方库的复杂性和潜在的兼容性问题。对于那些需要在不支持外部库的环境中使用Base64编码的LabVIEW项目来...
中文字符在计算机中通常以多字节形式存储,因此在进行Base64编码时,需要特殊处理以确保中文字符能够正确无误地被编码和解码。"完美支持中文"意味着该Base64工具不仅能够处理ASCII字符,还能够正确处理UTF-8编码的...
在JavaScript中,Base64编码和解码是经常用到的功能,尤其是在处理图像、JSON数据或者进行跨平台通信时。 ### Base64编码原理 Base64编码的基本思想是将每3个字节(24位)的数据转换为4个6位的字符(共24位),然后...