java中的编码与字符集

编码问题是跨系统交流的基本

下面有两个例子

  1. 在linux下用java创建了一个文件(这里默认代码里没有指定编码),里面包括英文和中文,然后在windows下同样用java读取这个文件,并输出,结果中文出现了乱码
  2. android手机和电脑的两个java程序进行信息交流,中文都是乱码。

网页大部分是用utf8编码的,在html头几行有charset的信息,在对下载下来的网页进行解析时,要注意编码,谷歌百度在对搜索结果的解析时也是用utf8的

java中的编码

字符串在java中统一用unicode表示( 即UTF-16 LE) 。如果源码文件是GBK编码, 操作系统(windows)默认的环境编码为GBK,那么编译时, JVM将 按照GBK编码将字节数组解析成字符,然后将字符转换为unicode格式的字节数组,作为内部存储,即String中有一个value属性是char数组,用于保存字符串内容,使用Unicode编码,java中的char类型为2个字节,对应UTF-16。

java编译器采用utf8,即class文件的存储是用utf8,因为相对于utf16,utf8在处理英文占用内存小,而程序大部分都是英文。IDE设置的编码方式用于存取java源文件,对于在不同系统平台上共享代码很重要。jvm运行时的编码方式是utf16,即jvm用utf8从class文件读取程序后再转化为utf16编码的字符串,因为utf16是2个字节,统一的长度更方便jvm申请数组、计算字符串长度、执行索引操作等操作。

//Unicode只是java的内部存储形式。当调用字符串的getBytes时,默认还是会使用系统的默认字符集获取字节数组。如默认字符集是UTF-8,则下面字符串得到的其实是字符串UTF-8编码的字节数组
"字符串".getBytes()

查看getByte和String中保存的char数组之间的差异


public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
   
    String s = new String("字符");
    Field field = String.class.getDeclaredField("value");
    field.setAccessible(true);
    System.out.println(bytes2hex(s.getBytes("UTF-8"))); //UTF-8编码 e5ad97e7aca6
    char[] charArr = (char[])field.get(s);
    System.out.println(char2hex(charArr)); //Unicode编码 5b577b26
}

//将byte数组转为16进制表示
public static String bytes2hex(byte[] bytes)
{
    StringBuilder sb = new StringBuilder();
    String tmp = null;
    for (byte b : bytes)
    {
        // 将每个字节与0xFF进行与运算
        tmp = intToHex(0xFF & b);
        sb.append(tmp);
    }

    return sb.toString();

}

//将char数组转为16进制表示
public static String char2hex(char[] charArr)
{
    StringBuilder sb = new StringBuilder();
    String tmp = null;
    for (char c : charArr)
    {
        tmp = intToHex(c);
        sb.append(tmp);
    }

    return sb.toString();

}

public static String intToHex(int i) {
    // 借助于Integer再转化为16进制
    String tmp = Integer.toHexString(i);
    if (tmp.length() == 1)// 每个字节8位,转为16进制标志,2个16进制位
    {
        tmp = "0" + tmp;
    }
    return tmp;
}

当打印字符串时,JVM 根据操作系统本地的语言环境,将unicode转换为GBK,然后操作系统将GBK格式的内容显示出来。

当源码文件是UTF-8, 我们需要通知编译器源码的格式,javac -encoding utf-8 … , 编译时,JVM按照utf-8 解析成字符,然后转换为unicode格式的字节数组, 那么不论源码文件是什么格式,同样的字符串,最后得到的unicode字节数组是完全一致的,显示的时候,也是转成系统默认字符集来显示(跟OS环境有关)

Unicode

Unicode 是一种字符集,Unicode 的学名是 “Universal Multiple-Octet Coded Character Set”,简称为 UCS。UCS 可以看作是 “Unicode Character Set” 的缩写。2016-06-21 颁发的 Unicode 9.0 共收录 128,237 个字。

unicode的 2 字节形式通常称作 UCS-2。然而,受制于 2 字节数量的限制,UCS-2 只能表示最多 65536 个字符。Unicode 的 4 字节形式被称为 UCS-4 或 UTF-32,能够定义 Unicode 的全部扩展,最多可定义 100 万个以上唯一字符。

UCS-4 是一个更大的尚未填充完全的 31 位字符集,加上恒为 0 的首位,共需占据 32 位,即 4 字节。理论上最多能表示 2^31 个字符,完全可以涵盖一切语言所用的符号。

UCS 只是规定如何编码,并没有规定如何传输、保存这个编码。例如 “汉” 字的 UCS 编码是 6C49,我可以用 4 个
ascii 数字来传输、保存这个编码;也可以用 utf-8 编码 3 个连续的字节 E6 B1 89 来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16 都是被广泛接受的方案。UTF 是 “UCS Transformation Format” 的缩写。

编码转换

网上流传着一种错误的方法:
GBK => UTF-8: new String(s.getBytes(“GBK”) , "UTF-8); ,这种方式是错误的

在 tomcat 下,却可以使用 new String(s.getBytes(“iso-8859-1”) ,“GBK”) 的原因:
tomcat 默认使用 iso-8859-1 编码。如果原本字符串是 GBK 的,tomcat 传输过程中,将 GBK 转成 iso-8859-1 了。 而 iso-8859-1 是单字节编码的,这种转换不会对原来的字节数组做任何改变。之前用 GBK 编码转成 iso-8859-1 后编码内容完全没变, 所以s.getBytes(“iso-8859-1”)实际上还是原来 GBK 的编码内容。

LANG=C

linux下的LANG=C是最早最简单的C语言环境(标准ASCII码)

当不加LANG=C的时候使用机器或者登陆到机器的客户端自动设定的语言环境,一般多见的是zh_CN.UTF-8或者en_US.GBK,不论哪一种他们的字母表都是aA交错顺序。在路径匹配中也就是[a-z]包含了英文字母的大小写。

Properties文件

properties配置文件使用的是unicode码。不可以直接在里面写中文,JDK提供了native2ascii这个方法。

  1. 只转换特定字符

    在控制台中可以输入汉字回车后,就可以看到转义的字符了。Ctrl+C退出。

  2. 转换properties文件

native2ascii allMessages_zh_CN.input.properties allMessages_zh_CN.properties
  1. 反向单一properties文件
native2ascii -reverse allMessages_zh_CN.properties allMessages_zh_CN.txt

使用java程序转换

public static String str2unicode(final String gbString) {
    char[] utfBytes = gbString.toCharArray();
    String unicodeBytes = "";
    for (int byteIndex = 0; byteIndex < utfBytes.length; byteIndex++) {
        String hexB = Integer.toHexString(utfBytes[byteIndex]);
        if (hexB.length() <= 2) {
            hexB = "00" + hexB;
        }
        unicodeBytes = unicodeBytes + "\\\\u" + hexB;
    }
    return unicodeBytes;
}

参考资料

Java 正确的做字符串编码转换
LANG=C有何具体作用?
Unicode(UTF-8, UTF-16)令人混淆的概念
unicode编码是占用几个字节?
java使用unicode为默认编码是什么意思

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值