我有这样一个java类:
public class UnicodeTest {
public static void main(String[] args) {
String s = "中";
String s1 = "";
System.out.println(s.length());
System.out.println(s1.length());
System.out.println(s1.toCharArray().length);
}
}
然后我使用xxd查看已编译的类文件:
0000000: cafe babe 0000 0032 0031 0700 0201 000b .......2.1......
0000010: 556e 6963 6f64 6554 6573 7407 0004 0100 UnicodeTest.....
0000020: 106a 6176 612f 6c61 6e67 2f4f 626a 6563 .java/lang/Objec
0000030: 7401 0006 3c69 6e69 743e 0100 0328 2956 t...<init>...()V
0000040: 0100 0443 6f64 650a 0003 0009 0c00 0500 ...Code.........
0000050: 0601 000f 4c69 6e65 4e75 6d62 6572 5461 ....LineNumberTa
0000060: 626c 6501 0012 4c6f 6361 6c56 6172 6961 ble...LocalVaria
0000070: 626c 6554 6162 6c65 0100 0474 6869 7301 bleTable...this.
0000080: 000d 4c55 6e69 636f 6465 5465 7374 3b01 ..LUnicodeTest;.
0000090: 0004 6d61 696e 0100 1628 5b4c 6a61 7661 ..main...([Ljava
00000a0: 2f6c 616e 672f 5374 7269 6e67 3b29 5608 /lang/String;)V.
00000b0: 0011 0100 03e4 b8ad 0800 1301 0006 eda1 ................
00000c0: a4ed b480 0900 1500 1707 0016 0100 106a ...............j
00000d0: 6176 612f 6c61 6e67 2f53 7973 7465 6d0c ava/lang/System.
00000e0: 0018 0019 0100 036f 7574 0100 154c 6a61 .......out...Lja
00000f0: 7661 2f69 6f2f 5072 696e 7453 7472 6561 va/io/PrintStrea
0000100: 6d3b 0a00 1b00 1d07 001c 0100 106a 6176 m;...........jav
0000110: 612f 6c61 6e67 2f53 7472 696e 670c 001e a/lang/String...
0000120: 001f 0100 066c 656e 6774 6801 0003 2829 .....length...()
0000130: 490a 0021 0023 0700 2201 0013 6a61 7661 I..!.#.."...java
0000140: 2f69 6f2f 5072 696e 7453 7472 6561 6d0c /io/PrintStream.
0000150: 0024 0025 0100 0770 7269 6e74 6c6e 0100 .$.%...println..
0000160: 0428 4929 560a 001b 0027 0c00 2800 2901 .(I)V....'..(.).
0000170: 000b 746f 4368 6172 4172 7261 7901 0004 ..toCharArray...
0000180: 2829 5b43 0100 0461 7267 7301 0013 5b4c ()[C...args...[L
0000190: 6a61 7661 2f6c 616e 672f 5374 7269 6e67 java/lang/String
00001a0: 3b01 0001 7301 0012 4c6a 6176 612f 6c61 ;...s...Ljava/la
00001b0: 6e67 2f53 7472 696e 673b 0100 0273 3101 ng/String;...s1.
00001c0: 000a 536f 7572 6365 4669 6c65 0100 1055 ..SourceFile...U
00001d0: 6e69 636f 6465 5465 7374 2e6a 6176 6100 nicodeTest.java.
00001e0: 2100 0100 0300 0000 0000 0200 0100 0500 !...............
00001f0: 0600 0100 0700 0000 2f00 0100 0100 0000 ......../.......
0000200: 052a b700 08b1 0000 0002 000a 0000 0006 .*..............
0000210: 0001 0000 0002 000b 0000 000c 0001 0000 ................
0000220: 0005 000c 000d 0000 0009 000e 000f 0001 ................
0000230: 0007 0000 0078 0002 0003 0000 0026 1210 .....x.......&..
0000240: 4c12 124d b200 142b b600 1ab6 0020 b200 L..M...+..... ..
0000250: 142c b600 1ab6 0020 b200 142c b600 26be .,..... ...,..&.
0000260: b600 20b1 0000 0002 000a 0000 001a 0006 .. .............
0000270: 0000 0005 0003 0006 0006 0007 0010 0008 ................
0000280: 001a 0009 0025 000a 000b 0000 0020 0003 .....%....... ..
0000290: 0000 0026 002a 002b 0000 0003 0023 002c ...&.*.+.....#.,
00002a0: 002d 0001 0006 0020 002e 002d 0002 0001 .-..... ...-....
00002b0: 002f 0000 0002 0030 ./.....0
我找到了中文字符&#34;中&#34;在第12行03e4 b8ad
,unicode U + 4E2D,其中UTF-8为E4 B8 AD
,但我找不到另一个字符&#34;&#34;,unicode U + 29100,我期待的类似于&#34; 04 F0 A9 84 80&#34;,为什么?
答案 0 :(得分:3)
让我们使用javap。
首先编译
Post: /_aliases
{
"actions" : [
{ "remove" : { "index" : "*", "alias" : "nameOfAlias" } }
]
}
然后拆开
javac UnicodeTest.java
(截断到相关部分):
javap -v UnicodeTest.class
常量池中的项目#20正是您要找的。 p>
现在,您可以检查JVM class file format。
Constant pool:
#1 = Methodref #9.#18 // java/lang/Object."<init>":()V
#2 = String #19 // 中
#3 = String #20 //
#4 = Fieldref #21.#22 //
... truncated
#17 = Utf8 UnicodeTest.java
#18 = NameAndType #10:#11 // "<init>":()V
#19 = Utf8 中
#20 = Utf8
数据结构为CONSTANT_Utf8_info
Utf8
标签是CONSTANT_Utf8(CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
)。长度为01
,字节为00 06
根据unicode查找,提到的字符应该有代码点0x29100。
现在回到JVM规范。
代码点高于U + FFFF的字符(所谓的补充 字符)通过分别编码两个代理来表示 UTF-16表示的代码单元。每个代理代码 单位由三个字节表示。这意味着补充 字符由六个字节u,v,w,x,y和z表示:
我不会在此处粘贴内容,因为它太长了,但您可以查看表4.12。在CONSTANT_Utf8_info信息下(上面的链接)
这就是为什么它长达6个字节。
现在让我们采用公式
ed a1 a4 ed b4 80
通过替换0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) +
((y & 0x0f) << 6) + (z & 0x3f)
,v
,w
和y
输出为168192(10) 0x29100 ,这是预期的代码点
答案 1 :(得分:1)
您可以使用的经典技术是通过其他方式更改值,并检查hex-dump的差异。这已经在八十年代被用来了,当你&#34; hack&#34;为游戏保存游戏以增加角色扮演角色的属性等。
我将字符改为a
,似乎可以在偏移量0xBE-0xC3处找到该字符,其值为ED A1 A4 ED B4 80
。我必须查看其中的细节,以便能够解释为什么值与您预期的值不同,但Java对Unicode的原始支持限制为两个字节(这是{{1定义了类型。具有3个字节或更多字节的Unicode字符需要在字节码中以特定方式进行编码,以告诉ClassLoader它需要以不同的方式处理。