字符集与编码--Java string.length 与 char 类型

这篇博客用于记录学习字符编码过程中得到的结论,不做过多的理论讲述。

参考资料:字符集与编码(五)

1. char 类型

在Java中,char类型存放一个用UTF-16编码代码单元(16位)表示的字符,用于表示单个字符,通常用来表示字符常量。例如:‘A‘是编码为65所对应的字符常量。

与"A"不同,"A"是一个包含字符A的字符串。Unicode代码点可以表示为十六进制值,其范围从\u0000到\uFFFF。例如:\u2122表示注册符号,\u03C0表示希腊字母π。

Java 中的 char 使用了 16 位,它可以放一个英文字符,一个中文字符,因为这些字符都在 BMP 中,如果一个字符来自增补平面,那么它将无法放入 char 中。

2. 增补字符的转义表示

另外我们可以以转义的代理对的方式表示增补字符,这样可以避免字库方面的问题。

当然了,任何字符都可以用转义的方式表示,而不仅仅限于增补字符。如果你没有安装输入法,可以简单使用 \u4F60 来表示“你”这个字符。

注:如果担心传输中出现乱码,可使用转义的表示,这时只含有 ASCII 字符,不过这种表示法效率很低。比如我们要用“\, u, 4, F, 6, 0”整整 6 个 ASCII 字符才能表示“你”这个汉字,也即要用 6 字节才能表示。而直接表示的话,用 GBK 只要两字节,而用 UTF-8 也不过是三字节。

例如 U+1D11E,写成 UTF-16 的代理对是(D8 34 DD 1E)

Java中的转义表示始终是以 \u 后面接4个16进制数字为界的(其实就是UTF-16的代码单元),你不能简单像码点那样写成 \u1D11E,这种写法相当于 "\u1D11" + "E",即只对前面4位 1D11 做转义,后面的E当成普通字母。如果要转义的字符码点超过 U+FFFF,我们需要两个一对的转义 \uD834\uDD1E 来表示,从这里我们也可以看到,所谓的转义表示其实就是UTF-16编码。

3. Java 中的 string.length 究竟指什么

如果你阅读一下 java 中 String 类中的 length 方法的说明,就会注意到以下文字:

Returns the length of this string. The length is equal to the number of Unicode code units in the string.

返回字符串的长度,这一长度等于字符串中的 Unicode 代码单元的数目。

我们知道 Java 语言里 String 在内存中以是 UTF-16 方式编码的,所以长度即是 UTF-16 的代码单元数目。

UTF-16 保存 BMP 中的字符时,使用了两个字节,也即一个代码单元。这就意味着,Java中所有的BMP字符长度都是1,无论是中文还是英文。这一点我想大家都没有疑问,代码示例如下:

@Test
public void testStringLength() {
    String str = "hello你好";
    assertThat(str.length()).isEqualTo(7);
}

不出所料,“hello你好” 有5个英文字符和2个中文字符,所以它的长度就是7。但我们知道,UTF-16 同样可以表示增补平面中的字符,而且用了四字节来表示,也即两个代码单元,那么一个增补平面中的字符,它的长度就是2。

4. string.codePointCount, string.codePointAt

根据上文描述,string.length 返回的是字符串中代码单元的个数,而不是实际的字符数量。Java 中获取字符数量可以使用 codePointCount,查看 API 说明如下:

Returns the number of Unicode code points in the specified text range of this String. The text range begins at the specified beginIndex and extends to the char at index endIndex - 1. Thus the length (in chars) of the text range is endIndex-beginIndex. Unpaired surrogates within the text range count as one code point each.

返回该字符串索引范围内的Unicode代码点的数量。索引范围为 [ beginIndex,  endIndex - 1] 闭区间,长度(以char为单位)就是endIndex-beginIndex。如果索引范围内有代理区的代码点,但不是以代理对的方式出现,则该码点认为是一个普通的Unicode代码点。

string.codePointAt API说明如下:

If the char value specified at the given index is in the high-surrogate range,  the following index is less than the length of this String, and the char value at the following index is in the low-surrogate range, then the supplementary code point corresponding to this surrogate pair is returned. Otherwise, the char value at the given index is returned.

如果index处的char值在高代理区,紧随其后的的index小于String.length,并且紧随其后的char值在低代理区,则返回此代理对表示的增补字符的码点。否则,返回index处的char值。

//UTF-16编码,使用两个代码单元表示增补字符,Unicode码点为U+1D11E。这里data表示两个码点构成的字符串
String data = "\uD834\uDD1E\uD834\uDD1E";
System.out.println(data.length());//输出4,可以看出string.length表示代码单元的个数

System.out.println(data.codePointAt(0)); //0x1D11E,输出Unicode代码点的值
System.out.println(data.codePointAt(1)); //0xDD1E,输出index处的char值
System.out.println(data.codePointAt(2)); //0x1D11E,输出Unicode代码点的值
System.out.println(data.codePointAt(3)); //0xDD1E,输出index处的char值
System.out.println(data.codePointCount(0, data.length()));// 2,表示Unicode代码点的数量
System.out.println(data.codePointCount(0, data.length()-1)); //2 

System.out.println(data.charAt(0)); // 0xD834
System.out.println(data.charAt(1)); // 0xDD1E,可以看出charAt表示代码单元的值


原文地址:https://www.cnblogs.com/lingxue3769/p/12218530.html

时间: 2024-10-10 08:47:41

字符集与编码--Java string.length 与 char 类型的相关文章

为啥在java中不要使用char类型

背景 最近项目中遇到一个问题,反复测试才发现问题出在了数据库中,由于使用了 Hibernate 这种ORM框架,因此,在java中写的 EntityBean 就可以直接通过ORM映射到Oracle数据库了,这也导致了很多的问题.当然,查了很多的资料,最终解决了这个问题,并且对Oracle的数据类型也有了一个更深层次的理解.下面是我的译文(原文是英文版的). 译文 要理解char类型,您首先必须了解Unicode编码模式.Unicode的发明克服了传统的字符编码方案的局限性.在Unicode出现之

JAVA数据类型中的char类型

1.JAVA中,char占2字节,16位.可在存放汉字 2.char赋值 char a='a'; //任意单个字符,加单引号. char a='中';//任意单个中文字,加单引号. char a=111;//整数.0~65535.十进制.八进制.十六进制均可.输出字符编码表中对应的字符. 注:只能放单个字符. 3.char运算 char类型是可以运算的因为char在ASCII等字符编码表中有对应的数值. 在JAVA中,对char类型字符运行时,直接当做ASCII表对应的整数来对待. 示例1: c

关于vector.size()和string.length() 的返回类型 size_type

今天写循环的时候碰到一个问题,发现:string.length()返回的类型是size_type.它是unsigned 类型.string::size_type它在不同的机器上,长度是可以不同的,并非固定的长度.但只要你使用了这个类型,就使得你的程序适合这个机器.与实际机器匹配. 如果你的机器是32位的,那么 int  len = str.length(),就侥幸对了. 如果你的机器是64位的,那么你就 被fuck 了. unsigned 类型两个数相减,如果是结果负数,就fuck了!显然不会得

string:本质是char类型的数组

一个字符串,可以看作是多个char组成的数组,本质就是char类型数组. 字符串是 Unicode 字符的有序集合,用于表示文本.所以String 对象是 Char 对象的有序集合,用于表示字符串.String 对象的值是该有序集合的内容,并且该值是不可变的.字符串本质是字符数组--这是一个非常重要的概念,了解这个概念就可以全面的理解和把握字符串的各种特征. 由于字符串是字符数组,因此字符串可以直接当数组使用,通过下标的模式访问字符串中的每一个字符,或者可以将字符串转为字符数组. static

字符集与编码--Java class文件的编码方式

1 public static void main(String[] args) throws UnsupportedEncodingException { 2 /** 3 * 1. char 和 String 在内存中保存都使用Java内码,也即UTF-16 4 * 2. char 在class文件中使用UTF-16表示 5 * 3. String在class文件中使用UTF-8表示 6 * 4. 序列化和Class文件中用"modified UTF-8",不是UTF-8.参考htt

Java String类练习题

题目:1. 给定一个字符串,判断该字符串中是否包含某个子串.如果包含,求出子串的所有出现位置.如:"abcbcbabcb34bcbd"中,"bcb"子串的出现位置为: 1,7,12.字符串和子串均由用户输入 2.给定一个长度,随机产生一个该长度的字符串,由大写,小写字母以及数字组成 Java中随机数的生成: java.util.Random r = new java.util.Random(); int a = r.nextInt(100): a 0-99的随机数

string、const char*、 char* 、char[]相互转换

转化总结如下: 源格式 目标格式 string const char* char* char[] string NULL const char*=string.c_str(); const char*=string.c_str(); char*=<const_cast><char*>(const char*); for(int i=0;i< string.length();i++) { char[i]=string[];} const char* string =const

Java String 常用方法

Java String class methods Java String toUpperCase() and toLowerCase() method Java String trim() method Java String startsWith() and endsWith() method Java String charAt() method Java String length() method Java String intern() method Java String valu

string 转换char类型

将string转换成char类型 const char *c = string.c_str() char转换string char *c_name = "char" string str_name = c_name