关于java字符的编码问题学习
来源:互联网 发布:平面装修设计软件 编辑:程序博客网 时间:2024/05/22 02:11
关于java字符串相关的字符集和编码方式不再解释,可以参见该篇文章[Java与字符编码问题详谈](http://hxraid.iteye.com/blog/559607),今天要说的是在java字符串转字节数组时的方式: String.getBytes()和Charset.encode(string)的区别;问题: String str = "我"; byte[] ba1 = str.getBytes("UTF-16"); printBytes(ba1);// 编码:0xfe 0xff 0x62 0x11; 前两个为BOM -------------华丽的分割线-------------------------------- Charset cs = CharSet.forName("UTF-16"); byte[] ba2 = cs.encode(str).array(); printBytes(ba2);//编码:0xfe 0xff 0x62 0x11 0x00; 最后多个0x00困扰我的问题来了,为什么会多了一个字节呢? 先来看看源码:一。String.getBytes()方法: public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); return StringCoding.encode(charsetName, value, 0, value.length); }在这里是直接调用StringCoding类的encode()方法。 static byte[] encode(String charsetName, char[] ca, int off, int len) throws UnsupportedEncodingException { StringEncoder se = 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); }StringCoding类先获取当前项目平台的编码, 如果和要求的指定编码方式不同,则通过制定编码方式获取字符集CharSet,进而生成StringEncode类(StringCoding的私有成员类), 而我们来看StringEncode类: private static class StringEncoder { private Charset cs; private CharsetEncoder ce; private final String requestedCharsetName; private final boolean isTrusted; ........... byte[] encode(char[] ca, int off, int len) { int en = scale(len, ce.maxBytesPerChar()); byte[] ba = new byte[en]; if (len == 0) return ba; if (ce instanceof ArrayEncoder) { int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); return safeTrim(ba, blen, cs, isTrusted); } else { ce.reset(); ByteBuffer bb = ByteBuffer.wrap(ba); CharBuffer cb = CharBuffer.wrap(ca, off, len); try { CoderResult cr = ce.encode(cb, bb, true); if (!cr.isUnderflow()) cr.throwException(); cr = ce.flush(bb); if (!cr.isUnderflow()) cr.throwException(); } catch (CharacterCodingException x) { // Substitution is always enabled, // so this shouldn't happen throw new Error(x); } return safeTrim(ba, bb.position(), cs, isTrusted); }可以看到StringEncode类的编码事实上也是调用了CharEncoder类来编码; 但注意的是StringCoding类的encode()方法中最后的这个safeTrim(ba, bb.position(), cs, isTrusted);这里会将ByteBuffer类中的值放到byte[]中,而这里的参数bb.position是将ByteBuffer中的有效的值放入了。 详情可参见[java.nio.ByteBuffer用法小结](http://blog.csdn.net/zhoujiaxq/article/details/22822289);
二. CharSet.encode(string)
首先CharSet.encode(),实际上是先生成CharsetEncode类,如下
public final ByteBuffer encode(CharBuffer cb) { try { return ThreadLocalCoders.encoderFor(this) .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .encode(cb); } catch (CharacterCodingException x) { throw new Error(x); // Can't happen } }
看这里如何编码, 如下:
public final ByteBuffer encode(CharBuffer in) throws CharacterCodingException { int n = (int)(in.remaining() * averageBytesPerChar()); ByteBuffer out = ByteBuffer.allocate(n); if ((n == 0) && (in.remaining() == 0)) return out; reset(); for (;;) { CoderResult cr = in.hasRemaining() ? encode(in, out, true) : CoderResult.UNDERFLOW; if (cr.isUnderflow()) cr = flush(out); if (cr.isUnderflow()) break; if (cr.isOverflow()) { n = 2*n + 1; // Ensure progress; n might be 0! ByteBuffer o = ByteBuffer.allocate(n); out.flip(); o.put(out); out = o; continue; } cr.throwException(); } out.flip(); return out;}
这两种方式的最终的编码其实都是调用了CharsetEncode类的方法:
CoderResult encode(CharBuffer in, ByteBuffer out,boolean endOfInput); 这里区别在于:
1>在调用方法前的参数上, 同一字符串调用时,参数ByteBuffer的会不同。 这取决于对传入的ByteBuffer的capacity属性的不同赋值,StringCoding的encode方法为:
int len = string.length;byte[] encode(char[] ca, int off, int len) { int en = scale(len, ce.maxBytesPerChar()); ...................
对ce(CharsetEncoder)的maxBytesPerChar;
而在CharSet类的encode方法中为:
CharBuffer in = CharBuffer.wrap(string);int n = (int)(in.remaining() * averageBytesPerChar());//注:string.length() = CharBuffer.wrap(string).remaining();ByteBuffer out = ByteBuffer.allocate(n);..............for (;;) { CoderResult cr = in.hasRemaining() ?encode(in, out, true) : CoderResult.UNDERFLOW; if (cr.isUnderflow()) cr = flush(out); if (cr.isUnderflow()) break; if (cr.isOverflow()) { n = 2*n + 1; // Ensure progress; n might be 0! //这里为了确保程序正常,当初始的ByteBuffer容量不足时,newCapacity = 2*oldCapacity+1; ByteBuffer o = ByteBuffer.allocate(n); out.flip(); o.put(out); out = o; continue; } cr.throwException(); } .....................
2>StringCoding对返回的结果ByteBuffer进行了处理。
byte[] encode(char[] ca, int off, int len) { int en = scale(len, ce.maxBytesPerChar()); if (len == 0) ................... if (ce instanceof ArrayEncoder) { ................... } else { ce.reset(); ByteBuffer bb = ByteBuffer.wrap(ba); CharBuffer cb = CharBuffer.wrap(ca, off, len); try { CoderResult cr = ce.encode(cb, bb, true); ................... } catch (CharacterCodingException x) { ................... } return safeTrim(ba, bb.position(), cs, isTrusted);
调用了safeTrim()方法,将ByteBuffer中的值复制到byte[]中。
而CharSet的encode方法直接就返回了ByteBuffer方法,而ByteBuffer中有的方法为array();如下:
Charset cs = CharSet.forName("UTF-16"); ByteBuffer bb = cs.encode(str); byte[] ba = bb.array(); printBytes(ba2);//编码:0xfe 0xff 0x62 0x11 0x00; 最后多个0x00
而ByteBuffer.array()方法是将ByteBuffer容器的每个元素都转变为数组中的值,这其中编码时,ByteBuffer容器没有被填满->容器中的某些值为0(因为ByteBuffer.allocate(n)初始化时会默认赋值);所有在字符编码时会,调用CharSet.encode()方法后, 对返回的结果ByteBuffer直接调用.array()方法会造成编码后的字节数组多出一个字节。
正确的处理CharSet.encode()返回结果的方式应如下:
String str = "我们";CharSet cs = CharSet.forName("UTF-16");//或'UTF-8'/'GBK'/'ASCII'等ByteBuffer bb = cs.encode(str);byte[] ba = new byte[bb.limit()];for(int i = 0;;i++){ if(bb.hasRemaining()) ba[i] = bb.get(); else{ break; } }printBytes(ba); //结果0xFE 0xff 0x62 0x11 0x4E 0xEC
- 关于java字符的编码问题学习
- 关于JAVA的字符编码问题
- 小议关于java字符编码的问题
- 关于字符编码的问题
- 关于字符编码的问题
- 关于字符编码的问题
- 关于Java字符编码几个容易弄错的问题
- 关于java中默认字符编码问题
- 关于字符编码问题
- 关于字符编码问题
- 关于字符编码问题
- 关于Qt的字符编码问题
- 关于C++程序的字符编码问题
- 关于Qt的字符编码问题
- 关于C++程序的字符编码问题
- 关于Qt的字符编码问题
- 关于java字符编码
- java:关于字符编码的试题
- Unix Network Programming(4)---TCP
- jrebel(转)
- c++虚拟函数virtual
- GitHub:代码是程序员沟通最直接的手段
- python的类属性和实例属性
- 关于java字符的编码问题学习
- iOS项目问题汇总<二>
- Hexagon DSP功能介绍
- [脚本]批量解压 重命名
- myeclipse自动提示不起作用
- 是技术博客还是心情博客?
- 类模版
- Android内存管理整理
- 不同变量类型的不同输出格式