Java string 字符集编码以及转换
基本概念
关于字符集的种类常用的有utf-8, unicode,gbk,gbk2312等,详细的字符集列表可以查看java.nio.charset.Charset类。
关键的字符集处理方法介绍如下:
String.getBytes() | 获取当前string表示的字符,在使用系统默认的字符集(关于系统默认的字符集后面详细讨论)时,所映射的二进制数据。不同的默认字符集生成的二进制数据是不同的,解码时只有使用与该默认字符集相同的字符集才能获得正确的字符。 |
String.getBytes(String charset) | 获取当前string表示的字符,在使用指定字符集时,所映射的二进制数据。传入不同的字符集生成的二进制数据是不同,解码时只有使用相同的字符集才能获得正确的字符。 |
new String(byte[] data) | 使用系统默认的字符集,将给定的二进制数据映射为相应的字符,并构造成一个string。如果系统默认的字符集不变那么可以通过String.getBytes()或String.getBytes(Charset.defaultCharset().displayName())来还原原来的二进制数据。 |
new String(byte[] data, String charset) | 使用给定的字符集,将给定的二进制数据映射为相应的字符,并构造成一个string。只有通过传入相同的字符集才能还原原来的二进制数据,String.getBytes("charset")。 |
日常使用场景及解决方案
场景1——修改JVM系统字符集
系统默认的字符集是指,JVM运行时调用java.nio.Charset.defaultCharset().displayName()所显示的字符集。我们有如下几种方式更改JVM在运行时的系统字符集:
方法1 | Properties pps=System. getProperties(); pps.put("file.encoding","<your-charset>"); System.setProperties(pps); |
方法2 | System.setProperty("file.encoding","<your-charset>"); |
方法3 | java -D file.encoding=<your-charset> |
上表中尖括弧斜体部分应该替换为你想要的字符集。
需要注意的是,如果是在运行时更改了字符集,那么再调用java.nio.Charset.defaultCharset().diaplayName()可能并不会变,因为Charset源码对default charset做了内容缓存,具体可查看Charset源码:
private static volatile Charset defaultCharset; |
因此要获取更改后的字符集编码,使用方法1或方法2后,需要使用System.getProperty("file.encoding")获取最新的字符集。在实际项目中,很多库都会默认调用String.getBytes(), 而该方法中使用的是java.nio.Charset.defaultCharset().displayName()来获取默认字符集。具体可查看String类和StringCoding类源码:
String类
/** |
StringCoding类
static byte[] encode(char[] ca, int off, int len) { |
综上所述,如果需要完美的修改系统默认的字符集,方法3最好。
场景2——网络通信的字符集编码
在开发中,前后端通信时出现乱码问题,特别是中文更易出现乱码。一般情况下,都是前后端默认的字符集和业务上需求的字符集不匹配。下面基于一个常见的具体情况,来做说明。
假设服务端默认的字符集编码为GBK,前端默认的字符集编码为GB2312。
如果双方都是明文通信,并且http请求和响应的header中都正确指定了字符集(正确指定是指,内容的编码与header中指定的字符集是匹配的),那么通信双方,都从对方发过来的header中拿到charset,然后调用new String(byte[] httpBodyRawData, charset)即可。
如果双方都是明文通信,但是并没有正确指定header中的字符集,那么就需要约定的字符集,如果约定的字符集为UTF-8, 那么调用new String(byte[] httpBodyRawData, "UTF-8")即可。
如果双方使用密文通信,约定的字符集为UTF-8。假设需要加密的字符串为toEncryStr, 解密后的原始数据为byte[] decryData。加密分为如下几种情况:
如果加密算法接收byte[]类型,那么需要:toEncryStr.getBytes("UTF-8")。
如果加密算法接收string类型,加密时调用String.getBytes("UTF-8"),那么需要做的转换是: new String(toEncryStr.getBytes("UTF-8"), "UTF-8");
该转换的意思是,首先将当前字符通过UTF-8字符集映射为二进制数据(此时只有用UTF-8解码才能正常显示),然后将二进制数据通过UTF-8字符集映射为相应字符(一定是正常字符)。之后加密时,调用String.getBytes("UTF-8")时会将转换后的字符(正常)还原为UTF-8编码的二进制数据。
解码比较简单,只需要:new String(decryData, "UTF-8")。// 约定的字符集为UTF-8
转载于:https://blog.51cto.com/9797337/1767774