java中 unicode编码问题

haorengoodman 2014-04-30 10:51:35

char ch = '我';
System.out.println(ch+" "+ch.SIZE+" "+String.valueOf(ch).getBytes("UNICODE").length+" UNICODE");

结果:我 16 4 UNICODE
.java 文件用的是UTF-8编码,UTF-8是Unicode的其中一个使用方式,
查资料,Unicode 有 2个字节 ,还有一个扩展的4字节 字符集,
'我' 的 unicode 16进制编码为 \u4e2d ,是2个字节,通过 UTF-8 编码 是 3个字节,这些都能理解

问题:通过如上的代码,显示 ‘我’ unicode编码占用 4个字节,这是为什么?如果占用4个字节,一个char(2个字节)怎么能存放的下这个4字节的中文字符呢?
...全文
243 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
haorengoodman 2014-05-04
  • 打赏
  • 举报
回复
http://bbs.csdn.net/topics/270062448
lxbccsu 2014-05-04
  • 打赏
  • 举报
回复
引用 5 楼 haorengoodman 的回复:
[quote=引用 3 楼 lxbccsu 的回复:] 忘说是 查看Character类; 示例下:

public class CharacterDemo {

	public static void main(String[] args) throws IOException {

		String str = "我";
		for(int i=0;i<str.length();i++){
			Character ch = str.charAt(i);
			System.out.println(ch+"   "+ch.SIZE+"   "+String.valueOf(ch).getBytes("UNICODE").length);
			System.out.println(byteToHex(String.valueOf(ch).getBytes("UNICODE")));
		}
	}

	
	public static String byteToHex(byte [] bytes){
		StringBuilder sb = new StringBuilder(4);
		for (int b : bytes) {
			sb.append(Integer.toHexString(b & 0x00FF | 0xFF00).substring(2, 4).toUpperCase());
			sb.append(" ");
		}
		return sb.toString();

	}
}
CharsetEncoder 中 544行 cr = encodeLoop(in, out);要被执行,根据前面得到的判断结果,应该回去执行 class UTF_16 extends Unicode 中的方法 private static class Encoder extends UnicodeEncoder { public Encoder(Charset charset) { super(charset, 0, true); } } 我想问一下 从 encodeLoop()-------> Encoder函数 这之间是不是还有别的操作,这两个函数的参数个数也不一样啊 --------------------------------------------------------根据源码,貌似最后一定回去执行 public abstract class UnicodeEncoder extends CharsetEncoder 中的方法 protected UnicodeEncoder(Charset charset, int i, boolean flag) { super(charset, 2.0F, flag ? 4F : 2.0F, i != 0 ? (new byte[] { -3, -1 }) : (new byte[] { -1, -3 })); usesMark = needsMark = flag; byteOrder = i; } 中完成 [/quote] 当然,如果你纠结于此,可以看下StringCoding的内部类StringEncoder 的 encode方法: static byte[] encode(String charsetName, char[] ca, int off, int len) throws UnsupportedEncodingException { StringEncoder se = (StringEncoder)deref(encoder); String csn = (charsetName == null) ? "ISO-8859-1" : charsetName; if ((se == null) || !(csn.equals(se.requestedCharsetName()) || csn.equals(se.charsetName()))) { se = null; try { Charset cs = lookupCharset(csn); if (cs != null) se = new StringEncoder(cs, csn); } catch (IllegalCharsetNameException x) {} if (se == null) throw new UnsupportedEncodingException (csn); set(encoder, se); } return se.encode(ca, off, len); } 当调用new StringEncoder(cs, csn)时,就会调用初始化 Charset 的 Encoder --> cs.newEncoder(); 这样很清楚了吧;
lxbccsu 2014-05-04
  • 打赏
  • 举报
回复
引用 5 楼 haorengoodman 的回复:
[quote=引用 3 楼 lxbccsu 的回复:] 忘说是 查看Character类; 示例下:

public class CharacterDemo {

	public static void main(String[] args) throws IOException {

		String str = "我";
		for(int i=0;i<str.length();i++){
			Character ch = str.charAt(i);
			System.out.println(ch+"   "+ch.SIZE+"   "+String.valueOf(ch).getBytes("UNICODE").length);
			System.out.println(byteToHex(String.valueOf(ch).getBytes("UNICODE")));
		}
	}

	
	public static String byteToHex(byte [] bytes){
		StringBuilder sb = new StringBuilder(4);
		for (int b : bytes) {
			sb.append(Integer.toHexString(b & 0x00FF | 0xFF00).substring(2, 4).toUpperCase());
			sb.append(" ");
		}
		return sb.toString();

	}
}
CharsetEncoder 中 544行 cr = encodeLoop(in, out);要被执行,根据前面得到的判断结果,应该回去执行 class UTF_16 extends Unicode 中的方法 private static class Encoder extends UnicodeEncoder { public Encoder(Charset charset) { super(charset, 0, true); } } 我想问一下 从 encodeLoop()-------> Encoder函数 这之间是不是还有别的操作,这两个函数的参数个数也不一样啊 --------------------------------------------------------根据源码,貌似最后一定回去执行 public abstract class UnicodeEncoder extends CharsetEncoder 中的方法 protected UnicodeEncoder(Charset charset, int i, boolean flag) { super(charset, 2.0F, flag ? 4F : 2.0F, i != 0 ? (new byte[] { -3, -1 }) : (new byte[] { -1, -3 })); usesMark = needsMark = flag; byteOrder = i; } 中完成 [/quote] 我对源代码的态度是:找关键的看,有目的的看,看得懂就看; 你上面说的源代码在sun开头的包中,而sun开头的包是不包含在JDK自带的那个src.zip中的;你需要自己去下载;或下载个反编译的工具也是可以的; 至于你上面说的源代码不明白,也简单说下: 其实你不需要关心UnicodeEncoder这个构造器方法; 关键就是上面提到的 encodeLoop(in, out),这个方法才是关键的; 这里有个参数 in 和 out,那其实简化就是in表示输入,out表示要输出的(out是ByteBuffer,in是CharBufer,从参数类型也可以看出);而且你也知道在CharsetEncoder类中,encodeLoop是protected abstract的,那自然是子类实现了; 你找到了UTF_16,但它是Charset的子类,但它定义了一个内部类 Encoder extends UnicodeEncoder ,而UnicodeEncoder有个encodeLoop() ,看到这里其它的可以忽略了吧,直接查看UnicodeEncoder 的 encodeLoop(); 如果是反编译的话,你会看到UnicodeEncoder 的 put方法是这样: private void put(char paramChar, ByteBuffer paramByteBuffer) { if (this.byteOrder == 0) { paramByteBuffer.put((byte)(paramChar >> '\b')); paramByteBuffer.put((byte)(paramChar & 0xFF)); } else { paramByteBuffer.put((byte)(paramChar & 0xFF)); paramByteBuffer.put((byte)(paramChar >> '\b')); } } 它的encodeLoop方法前面是这样的: protected CoderResult encodeLoop(CharBuffer paramCharBuffer, ByteBuffer paramByteBuffer) { int i = paramCharBuffer.position(); if (this.needsMark) { if (paramByteBuffer.remaining() < 2) return CoderResult.OVERFLOW; put(65279, paramByteBuffer); this.needsMark = false; } try { ... .... 看到这里,需要考虑下那个needsMark, 你上面提到的UTF_16 类中的内部类Encoder 会 super(charset, 0, true); 而这个needsMark = flag; 所以很显然是true了; 由于编译器对于一些常量是内嵌的,反编译后那个put方法了有点难理解,但可以自己动手测试下就清楚了: 如: byte[] b1 = {((byte)(65279 >> '\b'))}; byte[] b2 = {((byte)(65279 & 0xFF))}; System.out.print(byteToHex(b1) + ' '); System.out.print(byteToHex(b2)); System.out.println(); byte[] b11 = {((byte)('我' >> '\b'))}; byte[] b22 = {((byte)('我' & 0xFF))}; System.out.print(byteToHex(b11) + ' '); System.out.print(byteToHex(b22)); System.out.println(); 其实put方法中的:if (this.byteOrder == 0) 表示的是高字节在前的意思; 如果你是附加源代码的方式查看,那就非常的明确了;
Kenzson 2014-05-01
  • 打赏
  • 举报
回复
引用 6 楼 haorengoodman 的回复:
为毛 jdk源码中没有 sun.nio.cs.UnicodeEncoder 的源码?
不是没有,而是不给你直接用
haorengoodman 2014-04-30
  • 打赏
  • 举报
回复
为毛 jdk源码中没有 sun.nio.cs.UnicodeEncoder 的源码?
haorengoodman 2014-04-30
  • 打赏
  • 举报
回复
引用 3 楼 lxbccsu 的回复:
忘说是 查看Character类; 示例下:

public class CharacterDemo {

	public static void main(String[] args) throws IOException {

		String str = "我";
		for(int i=0;i<str.length();i++){
			Character ch = str.charAt(i);
			System.out.println(ch+"   "+ch.SIZE+"   "+String.valueOf(ch).getBytes("UNICODE").length);
			System.out.println(byteToHex(String.valueOf(ch).getBytes("UNICODE")));
		}
	}

	
	public static String byteToHex(byte [] bytes){
		StringBuilder sb = new StringBuilder(4);
		for (int b : bytes) {
			sb.append(Integer.toHexString(b & 0x00FF | 0xFF00).substring(2, 4).toUpperCase());
			sb.append(" ");
		}
		return sb.toString();

	}
}
CharsetEncoder 中 544行 cr = encodeLoop(in, out);要被执行,根据前面得到的判断结果,应该回去执行 class UTF_16 extends Unicode 中的方法 private static class Encoder extends UnicodeEncoder { public Encoder(Charset charset) { super(charset, 0, true); } } 我想问一下 从 encodeLoop()-------> Encoder函数 这之间是不是还有别的操作,这两个函数的参数个数也不一样啊 --------------------------------------------------------根据源码,貌似最后一定回去执行 public abstract class UnicodeEncoder extends CharsetEncoder 中的方法 protected UnicodeEncoder(Charset charset, int i, boolean flag) { super(charset, 2.0F, flag ? 4F : 2.0F, i != 0 ? (new byte[] { -3, -1 }) : (new byte[] { -1, -3 })); usesMark = needsMark = flag; byteOrder = i; } 中完成
Sailhere 2014-04-30
  • 打赏
  • 举报
回复
受教了,很详细
lxbccsu 2014-04-30
  • 打赏
  • 举报
回复
忘说是 查看Character类; 示例下:

public class CharacterDemo {

	public static void main(String[] args) throws IOException {

		String str = "我";
		for(int i=0;i<str.length();i++){
			Character ch = str.charAt(i);
			System.out.println(ch+"   "+ch.SIZE+"   "+String.valueOf(ch).getBytes("UNICODE").length);
			System.out.println(byteToHex(String.valueOf(ch).getBytes("UNICODE")));
		}
	}

	
	public static String byteToHex(byte [] bytes){
		StringBuilder sb = new StringBuilder(4);
		for (int b : bytes) {
			sb.append(Integer.toHexString(b & 0x00FF | 0xFF00).substring(2, 4).toUpperCase());
			sb.append(" ");
		}
		return sb.toString();

	}
}
lxbccsu 2014-04-30
  • 打赏
  • 举报
回复
引用 楼主 haorengoodman 的回复:

char ch = '我';
System.out.println(ch+"   "+ch.SIZE+"   "+String.valueOf(ch).getBytes("UNICODE").length+"   UNICODE");
结果:我 16 4 UNICODE .java 文件用的是UTF-8编码,UTF-8是Unicode的其中一个使用方式, 查资料,Unicode 有 2个字节 ,还有一个扩展的4字节 字符集, '我' 的 unicode 16进制编码为 \u4e2d ,是2个字节,通过 UTF-8 编码 是 3个字节,这些都能理解 问题:通过如上的代码,显示 ‘我’ unicode编码占用 4个字节,这是为什么?如果占用4个字节,一个char(2个字节)怎么能存放的下这个4字节的中文字符呢?
你查看下API就清楚了; 再有是了解下UTF-16编码的规则,如BE和LE的区别; 也可测试下:前面的两个字节其实是FE FF,表示高字节在前;
haorengoodman 2014-04-30
  • 打赏
  • 举报
回复
我自己顶一下,其实已经有点眉目了,但还是希望大神能够解释一下 ,

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧