Java String和hadoop的Text差异性比较
Text类似于String,UTF-8编码。采用整型储存长度,最大长度为2GB。
补充编码知识:
ASCII码
英语字母标点符号等256个字符,一个字节储存,取值0x00-0x7F;
UNICODE编码
则是对ASCII码的一个扩展,对所有文字的一个编码映射,是一个字符集,为每个文字分配一个id。0x000000-0x10FFFF 用这个区间映射所有文字。
UTF-8编码
UTF-8是一种编码规则,将Unicode的码位转换为字节编码。UTF(Unicode Transformation Format).例如UTF-8,采用变字节长度进行编码;大于2个字节时,如下表所示,第一个字节前面有几个1,表示有几个字节。X表示存储数据的位数。UTF-8使用1~4字节为每个字符编码:
·一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
·带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母则需要2字节编码(Unicode范围由U+0080~U+07FF)。
·其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
·其他极少使用的语言字符使用4字节编码。
Unicode编码(十六进制) |
UTF-8 字节流(二进制) |
000000-00007F |
0xxxxxxx |
000080-0007FF |
110xxxxx 10xxxxxx |
000800-00FFFF |
1110xxxx 10xxxxxx 10xxxxxx |
010000-10FFFF |
11110xxx10xxxxxx10xxxxxx10xxxxxx |
采用UTF-8对unicode码位进行编码的举例
「知」字的码位 U+77E5 属于第三行的范围:
7 7 E 5
0111 0111 1110 0101 二进制的 77E5
--------------------------
0111 011111 100101 二进制的 77E5
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行)
11100111 10011111 10100101 代入模版
E 7 9 F A 5
UTF-16编码
在上面的介绍中,提到了 Unicode 是一本很厚的字典,她将全世界所有的字符定义在一个集合里。这么多的字符不是一次性定义的,而是分区定义。每个区可以存放 65536 个(2^16)字符,称为一个平面(plane)。目前,一共有 17 个(2^5)平面,也就是说,整个 Unicode 字符集的大小现在是 2^21。
最前面的 65536 个字符位,称为基本平面(简称 BMP ),它的码点范围是从 0 到 2^16-1,写成 16 进制就是从 U+0000 到 U+FFFF。所有最常见的字符都放在这个平面,这是 Unicode 最先定义和公布的一个平面。剩下的字符都放在辅助平面(简称 SMP ),码点范围从 U+010000 到 U+10FFFF。
基本了解了平面的概念后,再说回到 UTF-16。UTF-16 编码介于 UTF-32 与 UTF-8 之间,同时结合了定长和变长两种编码方法的特点。它的编码规则很简单:基本平面的字符占用 2 个字节,辅助平面的字符占用 4 个字节。也就是说,UTF-16 的编码长度要么是 2 个字节(U+0000 到 U+FFFF),要么是 4 个字节(U+010000 到 U+10FFFF)。那么问题来了,当我们遇到两个字节时,到底是把这两个字节当作一个字符还是与后面的两个字节一起当作一个字符呢?
这里有一个很巧妙的地方,在基本平面内,从 U+D800 到 U+DFFF 是一个空段,即这些码点不对应任何字符。因此,这个空段可以用来映射辅助平面的字符。
辅助平面的字符位共有 2^20 个,因此表示这些字符至少需要 20 个二进制位。UTF-16 将这 20 个二进制位分成两半,前 10 位映射在 U+D800 到 U+DBFF,称为高位(H),后 10 位映射在 U+DC00 到 U+DFFF,称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。
因此,当我们遇到两个字节,发现它的码点在 U+D800 到 U+DBFF 之间,就可以断定,紧跟在后面的两个字节的码点,应该在 U+DC00 到 U+DFFF 之间,这四个字节必须放在一起解读。
接下来,以汉字"𠮷"为例,说明 UTF-16 编码方式是如何工作的。汉字"𠮷"(古汉字,不是吉利的吉)的 Unicode 码点为 0x20BB7,该码点显然超出了基本平面的范围(0x0000 - 0xFFFF),因此需要使用四个字节表示。首先用 0x20BB7 - 0x10000 计算出超出的部分0x10BB7,然后将其用 20 个二进制位表示(不足前面补 0 ),结果为0001 0000 10 11 1011 0111。接着,将前 10 位映射到 U+D800 到 U+DBFF 之间,后 10 位映射到 U+DC00 到 U+DFFF 即可。U+D800 对应的二进制数为 1101100000000000,直接填充后面的 10 个二进制位即可,得到 1101100001000010,转成 16 进制数则为 0xD842。同理U+DC00对应的二进制为1101110000000000,加上后10位的1101 1111 1011 0111低位为 0xDFB7。因此得出汉字"𠮷"的 UTF-16 编码为 0xD842 0xDFB7。常用\u D842\uDFB7表示。
字符 |
Unicode 码位 |
UTF-16 |
utf-8编码(绿色表示实际的二进制) |
节数 |
A |
0x000041 |
\u0041 |
0010 1001 |
1Byte |
ß |
0x0000DF |
\u00DF |
11000011 1001 1111 |
2Byte |
東 |
0x006771 |
\u6771 |
11100110 10 0111 01 1011 0001 |
3Byte |
𐐀 |
0x010400 |
\uD801\uDC00 |
11110000 1001 0000 100100 00 1000 0000 |
4Byte |
(3)Java String和hadoop的Text差异性比较
String(以字符位置来判断) |
Text(以字节偏移量来判断) |
indexOf("\uD801\uDC00" ) 返回字符在字符串中的位置3 |
find("\uD801\uDC00")返回的是UTF8编码的字节偏移量6 |
charAt(3)方法返回对应位置的char字符‘\uD801’ |
charAt(6) 输入是偏移量,输出是unicode的编码位置0x010400 |
codePointAt(3)返回unicode编码位置0x010400 |
|
length()返回char字符的长度1+1+1+2=5 |
|
getBytes().length 返回的是utf8编码占用长度1+2+3+4=10 |
t.getLength()返回UTF8编码所占空间1+2+3+4=10 |
import org.apache.hadoop.io.Text;
import org.junit.Assert;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
public class StringTextComparisonTest {
@Test
public void string() throws UnsupportedEncodingException {
String s = "\u0041\u00DF\u6771\uD801\uDC00"; //"Aß東𐐀"
System.out.println(s.codePointAt(1));
// indexOf( )方法返回char编码单元的索引位置
Assert.assertEquals(s.indexOf("\u0041"), 0);
Assert.assertEquals(s.indexOf("\u00DF"), 1);
Assert.assertEquals(s.indexOf("\u6771"), 2);
Assert.assertEquals(s.indexOf("\uD801\uDC00"), 3);
// charAt( )方法返回当前位置对应的char类型字符
Assert.assertEquals(s.charAt(0),'\u0041');
Assert.assertEquals(s.charAt(1),'\u00DF');
Assert.assertEquals(s.charAt(2),'\u6771');
Assert.assertEquals(s.charAt(3),'\uD801');
Assert.assertEquals(s.charAt(4),'\uDC00');
// codePointAt( )方法返回当前位置对应的char类型字符在Uncoide编码中的位置
Assert.assertEquals(s.codePointAt(0),0x0041); // utf-8编码的位置,十六进制表示
Assert.assertEquals(s.codePointAt(1),0x00DF);
Assert.assertEquals(s.codePointAt(2),0x6771);
Assert.assertEquals(s.codePointAt(3),0x10400);
System.out.println(s.toString());
// test length
Assert.assertEquals(s.length(), 5);
Assert.assertEquals(s.getBytes().length, 10);
}
@Test
public void text(){
// 这个字符串按照UTF-8方案编码后所占的字节数是1+2+3+4(没有\uDC00是因为找不到)
Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00");
System.out.println(t.charAt(1));
// find( )方法返回的是字符的字节偏移量
Assert.assertEquals(t.find("\u0041"), 0);
Assert.assertEquals(t.find("\u00DF"), 1);
Assert.assertEquals(t.find("\u6771"), 3);
Assert.assertEquals(t.find("\uD801\uDC00"), 6);
Assert.assertEquals(t.find("\uDC00"), -1);
// charAt( )方法返回对应的Unicode编码的位置
Assert.assertEquals(t.charAt(0),0x0041);
Assert.assertEquals(t.charAt(1),0x00DF);
Assert.assertEquals(t.charAt(3),0x006771);
Assert.assertEquals(t.charAt(6),0x10400);
System.out.println(t.toString());
Assert.assertEquals(t.getLength(), 10);
}
}
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix