ISO-8859-1转UTF-8的toString(String charsetName)坑

今天修复一个编码格式的bug,搜集了一些这方面的知识,总结一下吧。

需求:用户登录我们系统时候会去第三方系统获取用户基本信息,用户名(英文),job title等。这个流程是先读取信息,存储到我们的DB,然后从我们的DB读取数据进行展示。

bug: 当用户的job title含有®时,系统显示乱码。

原因分析(假设)

  1. 浏览器文字编码没有选择UTF-8
  2. 我们系统接收的就是乱码;
  3. 系统在转换存储的时候发出错误。

验证
假设一:选择UTF-8编码格式,仍然乱码
假设二:通过浏览器直接访问第三方系统,获取结果正常。
假设三:先排查转换,打印出结果确实是乱码。
把代码copy上来:

InputStream response = null;
ByteArrayOutputStream baos = null;
URL url = new URL(url_email);
HttpURLConnection urlConn = (HttpURLConnection) url
        .openConnection();
urlConn.setRequestProperty("Accept-Charset", "utf-8");
response = urlConn.getInputStream();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = response.read(buffer)) != -1) {
    baos2.write(buffer, 0, len);
}
System.out.println(baos2.toString("utf-8"));

按照UTF-8的设置,不应该有问题才对。
没有办法,只能把®转换之后的int值打印出来看看,结果返回174,二进制为10101110。

int i = -1;
while ((i = response.read()) != -1) {
    System.out.println(i);
    baos.write(i);
}

对比下UTF-8的编码格式,174的编码应该是11000010 10101110。,两者结果不一致,那肯定是baos2.toString(“utf-8”)出了问题。少年,还是先看看文档吧。
这里写图片描述
果然,文档说的很明白了,*toSting(String charsetName)不是将内容转换成charsetName的格式,而是以charsetName的编码格式进行解码,返回系统默认的编码格式。所以说,我们用A编码方式去解码B的编码格式,肯定解析不了出现乱码了。经查,第三方系统返回的编码格式是ISO-8859-1, 所以我们改成baos2.toString(“ISO-8859-1”),一切OK了。

最后一个问题,我已经设置了accept charsetName为UTF-8去接受数据,为啥还是ISO-8859-1呢。推测第三方系统没有处理我们的参数,直接默认返回ISO-8859-1了。

总结

  • 关于函数、参数的定义,要经常查Javadoc文档;
  • 不要理所当然的认为别人的系统会鸟你;自己的系统应该统一用UTF-8的格式,以防别人系统调用;
  • 在使用别人系统时,深入调研系统的支持编码格式,参数,甚至预测各种返回数据结果;
  • 遇到问题不要怕,一步步推进。

注释:
UTF-8的编码规则很简单,只有二条1
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
——————–+———————————————
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
——————–+———————————————
跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
下面,还是以汉字”严”为例,演示如何实现UTF-8编码。
已知”严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此”严”的UTF-8编码需要三个字节,即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,从”严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,”严”的UTF-8编码是”11100100 10111000 10100101”,转换成十六进制就是E4B8A5。


<think>好的,我现在需要解决用户的问题:如何用JavaScript将ISO-8859-1编码的字符串换为UTF-8编码。让我先理清楚这个换的过程。 首先,根据用户提供的引用内容,特别是引用[1]和引用[3],在Java中换时,通常的做法是使用字节数组作为桥梁。比如,将字符串以ISO-8859-1编码获取字节,然后再用UTF-8重新解码。那么JavaScript中是否有类似的方法呢? JavaScript中的字符串通常是UTF-16编码的,但处理其他编码可能需要使用到TextDecoder和TextDecoder API。不过,ISO-8859-1在JavaScript中有时也被称为Latin1,这可能是一个关键点。查阅资料发现,TextDecoder确实支持ISO-8859-1编码。 用户提到的引用[4]中提到了Node.js中使用Buffer来处理编码换。例如,读取文件时可以指定编码,或者将Buffer换为特定编码的字符串。这应该同样适用于浏览器环境,但可能需要不同的方法。 接下来,我需要考虑两种环境:浏览器和Node.js。因为JavaScript在不同的环境下有不同的API。比如在浏览器中,可以使用TextEncoder和TextDecoder,而在Node.js中则使用Buffer。 首先,在浏览器中的实现步骤可能如下: 1. 将ISO-8859-1字符串换为字节数组。这可以通过使用TextEncoder,但需要注意,TextEncoder默认是UTF-8的,所以可能需要先处理原始字符串。 或者,可能需要将每个字符的Latin1值提取出来,形成字节数组。 2. 然后将这个字节数组用UTF-8解码成字符串。 不过,这里有个问题,JavaScript中的字符串是UTF-16的,直接处理ISO-8859-1可能需要先将字符串换为对应的字节。例如,每个字符的charCodeAt(0)应该对应ISO-8859-1的字节值,因为ISO-8859-1是单字节编码。 因此,步骤可能是: 1. 遍历字符串的每个字符,获取其charCodeAt(0),这会得到0-255之间的值,因为ISO-8859-1是单字节的。 2. 将这些数值放入一个Uint8Array中。 3. 使用TextDecoder用UTF-8解码这个字节数组,得到正确的UTF-8字符串。 在Node.js环境中,处理起来更简单,因为Buffer对象支持直接指定编码。例如: 1. 用Buffer.from(str, 'latin1')ISO-8859-1字符串换为Buffer。 2. 然后用buffer.toString('utf8')换为UTF-8字符串。 不过用户的问题是关于JavaScript,可能需要区分浏览器和Node.js环境。用户提供的引用[4]是Node.js的例子,所以可能需要给出两种环境的解决方案。 另外,用户提到引用[3]中的例子,Java中将字符串换为ISO-8859-1后再换回来会出现乱码,类似的情况在JavaScript中如何处理?需要确保中间换步骤正确,比如正确的字节处理。 总结可能的步骤: 浏览器环境: 1. 将ISO-8859-1字符串换为字节数组。 2. 使用TextDecoder解码为UTF-8字符串。 Node.js环境: 1. 使用Buffer.from(str, 'latin1')换为Buffer。 2. 调用toString('utf8')得到UTF-8字符串。 需要验证这些步骤是否正确。例如,在浏览器中,如果有一个字符串,比如包含æµè¯这样的字符,如何正确换? 另外,需要注意,当从ISO-8859-1换为UTF-8时,如果原始字节序列在UTF-8中是有效的,那么换后的字符串应该正确。但如果原先是错误的编码换导致的乱码,可能需要更复杂的处理,但这可能不在当前问题范围内。 现在需要写出具体的代码示例,并确保正确性。例如,在浏览器中: function convertISO88591ToUTF8(isoString) { const bytes = new Uint8Array(isoString.length); for (let i = 0; i < isoString.length; i++) { bytes[i] = isoString.charCodeAt(i); } const decoder = new TextDecoder('utf-8'); return decoder.decode(bytes); } 在Node.js中: function convertISO88591ToUTF8(isoString) { const buffer = Buffer.from(isoString, 'latin1'); return buffer.toString('utf8'); } 这样应该能正确换。但需要测试是否有效,比如在Node.js中,将字符串从Latin1成Buffer,再UTF-8是否正确。 另外,用户提供的引用[3]中的Java例子码后出现乱码,可能因为直接使用字符串作为中间桥梁时出现了错误。因此,在JavaScript中正确的字节处理是关键,而不是直接使用字符串操作。 最后,要确保回答中提到的两种环境的方法,并给出示例代码,同时引用相关的参考资料,如引用[1]和引用[4]中的方法。</think>在JavaScript中,将ISO-8859-1编码的字符串换为UTF-8编码,需通过字节数组或`Buffer`作为桥梁处理。具体实现分浏览器和Node.js环境: --- ### **1. 浏览器环境** 使用`TextDecoder`和`TextEncoder`API实现: ```javascript function iso88591ToUtf8(isoString) { // 将ISO-8859-1字符串为字节数组 const bytes = new Uint8Array(isoString.length); for (let i = 0; i < isoString.length; i++) { bytes[i] = isoString.charCodeAt(i); // ISO-8859-1字符对应单字节值[^1] } // 用UTF-8解码字节数组 return new TextDecoder('utf-8').decode(bytes); } // 示例 const latin1Str = "æµè¯"; // 假设这是ISO-8859-1编码的乱码字符串 const utf8Str = iso88591ToUtf8(latin1Str); console.log(utf8Str); // 输出正确UTF-8字符(如原始内容为"测试") ``` --- ### **2. Node.js环境** 利用`Buffer`直接处理编码换: ```javascript function iso88591ToUtf8(isoString) { // 将字符串按Latin1编码为Buffer,再UTF-8字符串 return Buffer.from(isoString, 'latin1').toString('utf8'); } // 示例 const latin1Str = "æµè¯"; // 假设是ISO-8859-1编码字符串 const utf8Str = iso88591ToUtf8(latin1Str); console.log(utf8Str); // 输出正确UTF-8字符(如原始内容为"测试")[^3] ``` --- ### **关键原理** - **编码桥梁**:ISO-8859-1(Latin1)是单字节编码,每个字符对应0-255的字节值。通过提取字符的`charCodeAt()`值或`Buffer`直接获取字节,再以UTF-8重新解码[^4]。 - **编码兼容性**:UTF-8能表示更广泛的字符集,但需确保原始字节正确映射到目标编码的字符[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值