编程语言与字符编码
# 20.编程语言与字符编码
聊聊在编程语言中的字符,例如Java
# 一个字符占多少个字节?
得看情况,不同编码下情况不同。这里引用 肖国栋 (opens new window) 大佬的回答:
具体地讲,脱离具体的编码谈某个字符占几个字节是没有意义的。
就好比有一个抽象的整数“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码
}
2
3
4
5
6
问个面试题: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);
2
3
4
编译和运行结果:
str.getBytes().length: 2
str.getBytes("GBK").length: 2
str.getBytes("UTF-8").length: 3
2
3
注意事项:
- 可以在启动JVM时设置一个默认编码,例如:
java -Dfile.encoding=GBK Main
。不过如果你在代码里还是显示的指定了编码,还是以指定编码为准; - 如果你用IDE来运行代码,并且设置了工程的默认编码是UTF-8,那么此时运行程序的时候会加上
-Dfile.encoding=UTF-8
参数。 - 由于受启动参数及所在操作系统环境的影响,不带参数的 getBytes 方法通常是不建议使用的,最好是显式地指定参数以此获得稳定的预期行为。
# char类型
Java语言规范规定,Java的char类型是UTF-16的code unit,也就是一定是16位(2字节);
官方文档 (opens new window):
char
, whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1 (opens new window)).
值得一提的是,在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;
}
2
3
4
5
6
7
8
9
注释里说,会返回字符串中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());
}
}
2
3
4
5
6
7
8
9
对于大多数emoji表情来说,其码点都比较大,得用4个字节,因此在UTF-16中就得用2个字节,因此结果分别输出1,2
不会输入emoji的可以参考我的博客:输入法的技巧 (opens new window)
补充: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
}
}
2
3
4
5
6
7
# 代码
相关代码已上传到Gitee:01.JavaSE/05.OOP/15.character · 小林/LearnJava - 码云 - 开源中国 (opens new window)
# 参考
Java 语言中一个字符占几个字节? - 知乎 (opens new window)
本文主要参考了肖国栋 (opens new window) 大佬的回答,根据自己的测试而成