今晚上写代码玩,用到java.io.RandomAccessFile.writeUTF(String)函数,而文件默认保存为gbk,显然是乱码。突然想起来去看看存储编码规则,就去找了些文章了解writeUTF(String)的原理,在此记录。 
首先需要弄明白unicode与utf8的表示规则,搜到@Feng哥的一篇文章《字符编码笔记:ASCII,Unicode和UTF-8》,写的很明白,在此招录一段:

| Unicode符号范围 | UTF-8编码方式

| 0000 0000-0000 007F | 0xxxxxxx 
| 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 
| 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 
| 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
下面,还是以汉字"严"为例,演示如何实现UTF-8编码。 
已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx”。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严"的UTF-8编码是"11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

也就是将4E25(100 111000 100101)依次填充到(1110xxxx 10xxxxxx 10xxxxxx)的x位置里面!

文章中还强调的一点思想就是:Unicode是为统一世界多种编码问题而制定的统一编码,就是UTF-8只是Unicode的一种实现方式。

打印System.out.println(Integer.toHexString('严'));,打印结果为4e25,为Unicode编码.当使用RandomAccessFile.writeUTF(String), “严"以utf8个是写入文件。

下面是源码,以及我做的一些备注,以后回忆时候好用:

java.io.DataOutputStream.writeUTF(String, DataOutput)

static int writeUTF(String str, DataOutput out) throws IOException {
int strlen = str.length();
int utflen = 0;
int c, count = 0; /* 根据c的大小决定存储长度utflen的大小,最大65535字节,也就是64kb */
for (int i = 0; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
} if (utflen > 65535)
throw new UTFDataFormatException(
"encoded string too long: " + utflen + " bytes"); /*创建"合适"长度字节数组bytearr,下面的+2是因为需要在bytearr前两字节中存储数据长度utflen*/
byte[] bytearr = null;
if (out instanceof DataOutputStream) {
DataOutputStream dos = (DataOutputStream)out;
if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
dos.bytearr = new byte[(utflen*2) + 2];
bytearr = dos.bytearr;
} else {
bytearr = new byte[utflen+2];
} bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
/*如果是ascii码就直接存在bytearr里面了,毕竟老外写的源码,都是用ascii码机率比较高,就省去下面for中的判断了*/
int i=0;
for (i=0; i<strlen; i++) {
c = str.charAt(i);
if (!((c >= 0x0001) && (c <= 0x007F))) break;
bytearr[count++] = (byte) c;
}
/*上面满足不了就用下面的。。*/
for (;i < strlen; i++){
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
/*单字节,编码规则:0xxxxxxx ,ascii码处理*/
bytearr[count++] = (byte) c;
} else if (c > 0x07FF) {
/*三字节,编码规则:1110xxxx 10xxxxxx 10xxxxxx
参考上面的"严"字(100 111000 100101),则结果分别为(`1110`0100 `10`111000 `10`100101)*/
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
} else {
/*两字节,编码规则:110xxxxx 10xxxxxx*/
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
out.write(bytearr, 0, utflen+2);
return utflen + 2;
}

明白了写规则,然后就是读的规则了,反向理解就好。

java.io.DataInputStream.readUTF(DataInput)

public final static String readUTF(DataInput in) throws IOException {
int utflen = in.readUnsignedShort();
byte[] bytearr = null;
char[] chararr = null;
if (in instanceof DataInputStream) {
DataInputStream dis = (DataInputStream)in;
if (dis.bytearr.length < utflen){
dis.bytearr = new byte[utflen*2];
dis.chararr = new char[utflen*2];
}
chararr = dis.chararr;
bytearr = dis.bytearr;
} else {
bytearr = new byte[utflen];
chararr = new char[utflen];
} int c, char2, char3;
int count = 0;
int chararr_count=0; in.readFully(bytearr, 0, utflen); while (count < utflen) {
c = (int) bytearr[count] & 0xff;
if (c > 127) break;
count++;
chararr[chararr_count++]=(char)c;
} while (count < utflen) {
c = (int) bytearr[count] & 0xff;
switch (c >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
/* 0xxxxxxx*/
count++;
chararr[chararr_count++]=(char)c;
break;
case 12: case 13:
/* 110x xxxx 10xx xxxx*/
count += 2;
if (count > utflen)
throw new UTFDataFormatException(
"malformed input: partial character at end");
char2 = (int) bytearr[count-1];
if ((char2 & 0xC0) != 0x80)
throw new UTFDataFormatException(
"malformed input around byte " + count);
chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
(char2 & 0x3F));
break;
case 14:
/* 1110 xxxx 10xx xxxx 10xx xxxx */
count += 3;
if (count > utflen)
throw new UTFDataFormatException(
"malformed input: partial character at end");
char2 = (int) bytearr[count-2];
char3 = (int) bytearr[count-1];
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
throw new UTFDataFormatException(
"malformed input around byte " + (count-1));
chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
default:
/* 10xx xxxx, 1111 xxxx */
throw new UTFDataFormatException(
"malformed input around byte " + count);
}
}
// The number of chars produced may be less than utflen
return new String(chararr, 0, chararr_count);
}

一切就显得很明了了。

原文:http://my.oschina.net/diligentSt/blog/147933

android开发 WriteUTF与readUTF 原理的更多相关文章

  1. Android开发学习笔记(二)——编译和运行原理(1)

    http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...

  2. Android开发——断点续传原理以及实现

    0.  前言 在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件.但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录.本文原创, ...

  3. Android开发进阶——自定义View的使用及其原理探索

    在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...

  4. Android开发:图文分析 Handler通信机制 的工作原理

    前言 在Android开发的多线程应用场景中,Handler机制十分常用 下面,将图文详解 Handler机制 的工作原理 目录 1. 定义 一套 Android 消息传递机制 2. 作用 在多线程的 ...

  5. 《android开发艺术探索》读书笔记(四)--View工作原理

    接上篇<android开发艺术探索>读书笔记(三) No1: View的三大流程:测量流程.布局流程.绘制流程 No2: ViewRoot对应于ViewRootImpl类,它是连接Wind ...

  6. Android开发-之监听button点击事件

    一.实现button点击事件的方法 实现button点击事件的监听方法有很多种,这里总结了常用的四种方法: 1.匿名内部类 2.外部类(独立类) 3.实现OnClickListener接口 4.添加X ...

  7. Android开发之自定义组件和接口回调

    说到自定义控件不得不提的就是接口回调,在Android开发中接口回调用的还是蛮多的.在这篇博客开始的时候呢,我想聊一下iOS的自定义控件.在iOS中自定义控件的思路是继承自UIView, 在UIVie ...

  8. Android开发之基本控件和详解四种布局方式

    Android中的控件的使用方式和iOS中控件的使用方式基本相同,都是事件驱动.给控件添加事件也有接口回调和委托代理的方式.今天这篇博客就总结一下Android中常用的基本控件以及布局方式.说到布局方 ...

  9. Android开发学习之路-该怎么学Android(Service和Activity通信为例)

    在大部分地方,比如书本或者学校和培训机构,教学Android的方式都基本类似,就是告诉先上原理方法,然后对着代码讲一下. 但是,这往往不是一个很好的方法,为什么? ① 学生要掌握这个方法的用途,只能通 ...

随机推荐

  1. Linux安装VritualBox实现虚拟机win2003端口映射 支持远程

    1. 使用VNC登录到Linux系统 2. 安装VritualBox 找到VritualBox的软件包 这里的是run格式的 可以直接在终端运行  需要几分钟时间 3.VritualBox新建虚拟机 ...

  2. IOS基础——静态方法(类方法)和实例方法

    1.实例方法/动态方法 a).标识符:- b).调用方式:(实例对象    函数) c).实例方法在堆栈上. 2.静态方法/类方法 a).标识符:+ b).调用方式:(类    函数) c).静态方法 ...

  3. c#中判断对象为空的几种方式(字符串等)

    (1)先了解几个与空类型相关的关键字和对象  Null : 关键字表示不引用任何对象的空引用,它是所有引用类型变量的默认值,在2.0版本之前也就只有引用变量类型可以为null,如(string a=n ...

  4. js执行过程

    正如我们了解的一样,当我们书写了JS程序之后,打开浏览器,我们的代码就可以开始运行了(当然保证你的代码没有问题,才能按照你的预期进行执行).刚才说的是JS执行的一个大的环境,今天我们学习一下,JS在解 ...

  5. 为hbase新增节点

    为hbase增加新的节点,首先要为hadoop增加新新街点.因为我的做法是将datanode和regionserver放到一台物理机上.因此大体流程是: 1.克隆已经存在的regionserver虚拟 ...

  6. mouseout以及mouseover

    两个图层 红色图层代表outside蓝色图层代表inside dom结构如下 <div id="outside">      <div id="insi ...

  7. javascript绑定时间 含(IE)

    script language = "javascript" type = "text/javascript"> function test(){ win ...

  8. 在Silverlight宿主html页面添加按钮无法显示

    在建silverlight应用程序时宿主html中嵌入的silverlight时出现的问题: 预想效果: 实际效果: silverlight填满整个page的所以无法显示html中其他的控件 解决办法 ...

  9. Why java main function is declared as static type?

    一个暂且说的过去的解释 The method is static because otherwise there would be ambiguity: which constructor shoul ...

  10. Linux环境下常用regexp的使用

    正则表达式 REGular EXPression   的简写元字符 匹配次数 位置锚定 分组 --------------------------------------元字符. 匹配任意单个字符 [ ...