编程语言与字符编码

聊聊在编程语言中的字符,例如 Java

一个字符占多少个字节?

得看情况,不同编码下情况不同。这里引用 肖国栋 大佬的知乎回答:

具体地讲,脱离具体的编码谈某个字符占几个字节是没有意义的。

就好比有一个抽象的整数“42”,你说它占几个字节?这得具体看你是用 byte,short,int,还是 long 来存它。用 byte 存就占一字节,用 short 存就占两字节,int 通常是四字节,long 通常八字节。当然,如果你用 byte,受限于它有限的位数,有些数它是存不了的,比如 256 就无法放在一个 byte 里了。字符是同样的道理,如果你想谈“占几个字节”,就要先把编码说清楚。

同一个字符在不同的编码下可能占不同的字节。

就以你举的“字”字为例,“字”在 GBK 编码下占 2 字节,在 UTF-16 编码下也占 2 字节,在 UTF-8 编码下占 3 字节,在 UTF-32 编码下占 4 字节。

不同的字符在同一个编码下也可能占不同的字节。

“字”在 UTF-8 编码下占 3 字节,而“A”在 UTF-8 编码下占 1 字节。(因为 UTF-8 是变长编码)

Java 中的字符串

对于 String 类型,有这样一个方法:getBytes()​,其将一个字符串转化为 Byte 序列,并存储到新的 byte[]​ 数组:

String str2 = "abcdefg";
byte[] b = str2.getBytes();
for(int i = 0; i < b.length; i++){
  System.out.println(b[i]); //输出97.98.99.100...103, 就是 abcdefg 的 ASCII 码
}

问个面试题:new String("字").getBytes().length ​的值是多少?还是上面提到的那句话,得看编码。

在 UTF8 下,"字"​ 占 3 个; 而在 GBK 下,一个汉字占 2 个字节;如果 getBytes()​ 没有传参,那么默认使用操作系统编码,通常,Windows 系统下是 GBK,Linux 和 Mac 是 UTF-8:

String str = "严";
System.out.println("str.getBytes().length: "+ str.getBytes().length);
System.out.println("str.getBytes(\"GBK\").length: "+ str.getBytes("GBK").length);
System.out.println("str.getBytes(\"UTF-8\").length: "+ str.getBytes("UTF-8").length);

编译和运行结果:

str.getBytes().length: 2
str.getBytes("GBK").length: 2
str.getBytes("UTF-8").length: 3

注意事项:

  1. 可以在启动 JVM 时设置一个默认编码,例如:java -Dfile.encoding=GBK Main​。不过如果你在代码里还是显示的指定了编码,还是以指定编码为准;
  2. 如果你用 IDE 来运行代码,并且设置了工程的默认编码是 UTF-8,那么此时运行程序的时候会加上 -Dfile.encoding=UTF-8​ 参数。
  3. 由于受启动参数及所在操作系统环境的影响,不带参数的 getBytes​ 方法通常是不建议使用的,最好是显式地指定参数以此获得稳定的预期行为。

char 类型

Java 语言规范规定,Java 的 char 类型是 UTF-16 的 code unit,也就是一定是 16 位(2 字节):

官方文档char​, whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1).

值得一提的是,在 Java 设计之初,UTF-16 还是定长编码的,但是随着 Unicode 字符的增多,UTF-16 就改为变长编码了(2 个字节或 4 个字节)。 所以 char 中只能放 UTF-16 编码下只占 2 字节的那些字符,如果一个字符在 UTF-16 编码下占 4 字节,显然它是不能放到 char 中的。

Python 下的字符编码

Python2 默认用的编码是 ASCII,Python3 默认用的是 UTF8

分别使用 len("你好") 在 Python3 得到的结果是 2,在 Python2 得到的结果是 6(6 个字节)

Java 中的 String.length()

在 Java 中,可以通过 String.length()​ 计算字符串的长度。那么这个长度是什么呢?我们来看看 length() ​的注释和源码:

/**
 * Returns the length of this string.
 * The length is equal to the number of Unicode code units in the string.
 *
 * @return  the length of the sequence of characters represented by this object.
 */
public int length() {
    return value.length;
}

注释里说,会返回字符串中 Unicode unit 的数量。在 Java 中,使用的是 UTF-16,因此这个方法的功能可以解释为:返回字符串的长度,这一长度等于字符串中的 UTF-16 的代码单元的数目。

也就是说,如果一个字符使用 4 个字节,那么 String.length() ​长度是 2!

我们来测试下:

public class TestStringLength {
  public static void main(String[] args) {
    String str = "A";
    System.out.println(str.length());

    String str2 = "😀";
    System.out.println(str2.length());
  }
}

对于大多数 emoji 表情来说,其码点都比较大,得用 4 个字节,因此在 UTF-16 中就得用 2 个字节,因此结果分别输出 1, 2。

不会输入 emoji 的可以参考我的另一篇博客:输入法的技巧

补充:String.length()​ 返回的是 value.length​,这个 value 是什么呢?是一个 char 数组,前面我们也讲过 Java 的 char 类型是 UTF-16 的 code unit,也就是一定是 16 位(2 字节):

 private final char value[];

String.codePointCount()

那么我们如何统计字符串有几个字符呢?可以用​ String.codePointCount()​​

文档说明:返回指定字符序列的文本范围内的 Unicode 代码点数量。

public class TestStringLength {
  public static void main(String[] args) {
    String str2 = "😂";
    System.out.println(str2.length());
    System.out.println(str2.codePointCount(0,str2.length())); //输出1
  }
}

代码

相关代码已上传到 Giteehttps://gitee.com/peterjxl/LearnJava/tree/master/src/01.JavaSE/05.OOP/15.character

posted @ 2024-06-20 10:13  peterjxl  阅读(10)  评论(0编辑  收藏  举报