java io系列14之 DataInputStream(數據輸入流)的認知、源碼和示例



本章介紹DataInputStream。我們先對DataInputStream有個大致認識,然后再深入學習它的源碼,最后通過示例加深對它的了解。

轉載請注明出處:http://www.cnblogs.com/skywang12345/p/io_14.html

DataInputStream 介紹

DataInputStream 是數據輸入流。它繼承於FilterInputStream。
DataInputStream 是用來裝飾其它輸入流,它“允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型”。應用程序可以使用DataOutputStream(數據輸出流)寫入由DataInputStream(數據輸入流)讀取的數據。

DataInputStream 函數列表

DataInputStream(InputStream in)
final int     read(byte[] buffer, int offset, int length)
final int     read(byte[] buffer)
final boolean     readBoolean()
final byte     readByte()
final char     readChar()
final double     readDouble()
final float     readFloat()
final void     readFully(byte[] dst)
final void     readFully(byte[] dst, int offset, int byteCount)
final int     readInt()
final String     readLine()
final long     readLong()
final short     readShort()
final static String     readUTF(DataInput in)
final String     readUTF()
final int     readUnsignedByte()
final int     readUnsignedShort()
final int     skipBytes(int count)

 

DataInputStream.java源碼分析(基於jdk1.7.40)

  1 package java.io;
  2 
  3 public class DataInputStream extends FilterInputStream implements DataInput {
  4 
  5     // 構造函數。
  6     public DataInputStream(InputStream in) {
  7         super(in);
  8     }
  9 
 10     private byte bytearr[] = new byte[80];
 11     private char chararr[] = new char[80];
 12 
 13     // 從“數據輸入流”中讀取一個字節
 14     public final int read(byte b[]) throws IOException {
 15         return in.read(b, 0, b.length);
 16     }
 17 
 18     // 從“數據輸入流”中讀取數據並存儲到字節數組b中。
 19     // off是字節數組b中開始存儲元素的起始位置。
 20     // len是讀取字節的個數。
 21     public final int read(byte b[], int off, int len) throws IOException {
 22         return in.read(b, off, len);
 23     }
 24 
 25     // 從“數據輸入流”中讀取數據並填滿字節數組b中;沒有填滿數組b則一直讀取,直到填滿位置。
 26     // 從字節數組b的位置0開始存儲,並且讀取的字節個數等於b的長度。
 27     public final void readFully(byte b[]) throws IOException {
 28         readFully(b, 0, b.length);
 29     }
 30 
 31     // 從“數據輸入流”中讀取數據並存儲到字節數組b中;若沒讀取len個字節,直到一直讀取直到讀取完len個字節為止。
 32     public final void readFully(byte b[], int off, int len) throws IOException {
 33         if (len < 0)
 34             throw new IndexOutOfBoundsException();
 35         int n = 0;
 36         while (n < len) {
 37             int count = in.read(b, off + n, len - n);
 38             if (count < 0)
 39                 throw new EOFException();
 40             n += count;
 41         }
 42     }
 43 
 44     // 跳過n個字節
 45     public final int skipBytes(int n) throws IOException {
 46         int total = 0;
 47         int cur = 0;
 48 
 49         while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
 50             total += cur;
 51         }
 52 
 53         return total;
 54     }
 55 
 56     // 從“數據輸入流”中讀取boolean類型的值
 57     public final boolean readBoolean() throws IOException {
 58         int ch = in.read();
 59         if (ch < 0)
 60             throw new EOFException();
 61         return (ch != 0);
 62     }
 63 
 64     // 從“數據輸入流”中讀取Byte類型的值
 65     public final byte readByte() throws IOException {
 66         int ch = in.read();
 67         if (ch < 0)
 68             throw new EOFException();
 69         return (byte)(ch);
 70     }
 71 
 72     // 從“數據輸入流”中讀取“無符號的Byte類型”的值,即讀取值為正數的byte值
 73     public final int readUnsignedByte() throws IOException {
 74         int ch = in.read();
 75         if (ch < 0)
 76             throw new EOFException();
 77         return ch;
 78     }
 79 
 80     // 從“數據輸入流”中讀取“short類型”的值
 81     public final short readShort() throws IOException {
 82         int ch1 = in.read();
 83         int ch2 = in.read();
 84         if ((ch1 | ch2) < 0)
 85             throw new EOFException();
 86         return (short)((ch1 << 8) + (ch2 << 0));
 87     }
 88 
 89     // 從“數據輸入流”中讀取“無符號的short類型”的值
 90     public final int readUnsignedShort() throws IOException {
 91         int ch1 = in.read();
 92         int ch2 = in.read();
 93         if ((ch1 | ch2) < 0)
 94             throw new EOFException();
 95         return (ch1 << 8) + (ch2 << 0);
 96     }
 97 
 98     // 從“數據輸入流”中讀取“char類型”的值
 99     public final char readChar() throws IOException {
100         int ch1 = in.read();
101         int ch2 = in.read();
102         if ((ch1 | ch2) < 0)
103             throw new EOFException();
104         return (char)((ch1 << 8) + (ch2 << 0));
105     }
106 
107     // 從“數據輸入流”中讀取“int類型”的值
108     public final int readInt() throws IOException {
109         int ch1 = in.read();
110         int ch2 = in.read();
111         int ch3 = in.read();
112         int ch4 = in.read();
113         if ((ch1 | ch2 | ch3 | ch4) < 0)
114             throw new EOFException();
115         return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
116     }
117 
118     private byte readBuffer[] = new byte[8];
119 
120     // 從“數據輸入流”中讀取“long類型”的值
121     public final long readLong() throws IOException {
122         readFully(readBuffer, 0, 8);
123         return (((long)readBuffer[0] << 56) +
124                 ((long)(readBuffer[1] & 255) << 48) +
125                 ((long)(readBuffer[2] & 255) << 40) +
126                 ((long)(readBuffer[3] & 255) << 32) +
127                 ((long)(readBuffer[4] & 255) << 24) +
128                 ((readBuffer[5] & 255) << 16) +
129                 ((readBuffer[6] & 255) <<  8) +
130                 ((readBuffer[7] & 255) <<  0));
131     }
132 
133     // 從“數據輸入流”中讀取“float類型”的值
134     public final float readFloat() throws IOException {
135         return Float.intBitsToFloat(readInt());
136     }
137 
138     // 從“數據輸入流”中讀取“double類型”的值
139     public final double readDouble() throws IOException {
140         return Double.longBitsToDouble(readLong());
141     }
142 
143     private char lineBuffer[];
144 
145     @Deprecated
146     public final String readLine() throws IOException {
147         char buf[] = lineBuffer;
148 
149         if (buf == null) {
150             buf = lineBuffer = new char[128];
151         }
152 
153         int room = buf.length;
154         int offset = 0;
155         int c;
156 
157 loop:   while (true) {
158             switch (c = in.read()) {
159               case -1:
160               case '\n':
161                 break loop;
162 
163               case '\r':
164                 int c2 = in.read();
165                 if ((c2 != '\n') && (c2 != -1)) {
166                     if (!(in instanceof PushbackInputStream)) {
167                         this.in = new PushbackInputStream(in);
168                     }
169                     ((PushbackInputStream)in).unread(c2);
170                 }
171                 break loop;
172 
173               default:
174                 if (--room < 0) {
175                     buf = new char[offset + 128];
176                     room = buf.length - offset - 1;
177                     System.arraycopy(lineBuffer, 0, buf, 0, offset);
178                     lineBuffer = buf;
179                 }
180                 buf[offset++] = (char) c;
181                 break;
182             }
183         }
184         if ((c == -1) && (offset == 0)) {
185             return null;
186         }
187         return String.copyValueOf(buf, 0, offset);
188     }
189 
190     // 從“數據輸入流”中讀取“UTF類型”的值
191     public final String readUTF() throws IOException {
192         return readUTF(this);
193     }
194 
195     public final static String readUTF(DataInput in) throws IOException {
196         // 從“數據輸入流”中讀取“無符號的short類型”的值:
197         // 注意:UTF-8輸入流的前2個字節是數據的長度
198         int utflen = in.readUnsignedShort();
199         byte[] bytearr = null;
200         char[] chararr = null;
201 
202         // 如果in本身是“數據輸入流”,
203         // 則,設置字節數組bytearr = "數據輸入流"的成員bytearr
204         //     設置字符數組chararr = "數據輸入流"的成員chararr
205         // 否則的話,新建數組bytearr和chararr
206         if (in instanceof DataInputStream) {
207             DataInputStream dis = (DataInputStream)in;
208             if (dis.bytearr.length < utflen){
209                 dis.bytearr = new byte[utflen*2];
210                 dis.chararr = new char[utflen*2];
211             }
212             chararr = dis.chararr;
213             bytearr = dis.bytearr;
214         } else {
215             bytearr = new byte[utflen];
216             chararr = new char[utflen];
217         }
218 
219         int c, char2, char3;
220         int count = 0;
221         int chararr_count=0;
222 
223         // 從“數據輸入流”中讀取數據並存儲到字節數組bytearr中;從bytearr的位置0開始存儲,存儲長度為utflen。
224         // 注意,這里是存儲到字節數組!而且讀取的是全部的數據。
225         in.readFully(bytearr, 0, utflen);
226 
227         // 將“字節數組bytearr”中的數據 拷貝到 “字符數組chararr”中
228         // 注意:這里相當於“預處理的輸入流中單字節的符號”,因為UTF-8是1-4個字節可變的。
229         while (count < utflen) {
230             // 將每個字節轉換成int值
231             c = (int) bytearr[count] & 0xff;
232             // UTF-8的單字節數據的值都不會超過127;所以,超過127,則退出。
233             if (c > 127) break;
234             count++;
235             // 將c保存到“字符數組chararr”中
236             chararr[chararr_count++]=(char)c;
237         }
238 
239         // 處理完輸入流中單字節的符號之后,接下來我們繼續處理。
240         while (count < utflen) {
241             // 下面語句執行了2步操作。
242             // (01) 將字節由 “byte類型” 轉換成 “int類型”。
243             //      例如, “11001010” 轉換成int之后,是 “00000000 00000000 00000000 11001010”
244             // (02) 將 “int類型” 的數據左移4位
245             //      例如, “00000000 00000000 00000000 11001010” 左移4位之后,變成 “00000000 00000000 00000000 00001100”
246             c = (int) bytearr[count] & 0xff;
247             switch (c >> 4) {
248                 // 若 UTF-8 是單字節,即 bytearr[count] 對應是 “0xxxxxxx” 形式;
249                 // 則 bytearr[count] 對應的int類型的c的取值范圍是 0-7。
250                 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
251                     /* 0xxxxxxx*/
252                     count++;
253                     chararr[chararr_count++]=(char)c;
254                     break;
255 
256                 // 若 UTF-8 是雙字節,即 bytearr[count] 對應是 “110xxxxx  10xxxxxx” 形式中的第一個,即“110xxxxx”
257                 // 則 bytearr[count] 對應的int類型的c的取值范圍是 12-13。
258                 case 12: case 13:
259                     /* 110x xxxx   10xx xxxx*/
260                     count += 2;
261                     if (count > utflen)
262                         throw new UTFDataFormatException(
263                             "malformed input: partial character at end");
264                     char2 = (int) bytearr[count-1];
265                     if ((char2 & 0xC0) != 0x80)
266                         throw new UTFDataFormatException(
267                             "malformed input around byte " + count);
268                     chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
269                                                     (char2 & 0x3F));
270                     break;
271 
272                 // 若 UTF-8 是三字節,即 bytearr[count] 對應是 “1110xxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“1110xxxx”
273                 // 則 bytearr[count] 對應的int類型的c的取值是14 。
274                 case 14:
275                     /* 1110 xxxx  10xx xxxx  10xx xxxx */
276                     count += 3;
277                     if (count > utflen)
278                         throw new UTFDataFormatException(
279                             "malformed input: partial character at end");
280                     char2 = (int) bytearr[count-2];
281                     char3 = (int) bytearr[count-1];
282                     if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
283                         throw new UTFDataFormatException(
284                             "malformed input around byte " + (count-1));
285                     chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
286                                                     ((char2 & 0x3F) << 6)  |
287                                                     ((char3 & 0x3F) << 0));
288                     break;
289 
290                 // 若 UTF-8 是四字節,即 bytearr[count] 對應是 “11110xxx 10xxxxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“11110xxx”
291                 // 則 bytearr[count] 對應的int類型的c的取值是15 
292                 default:
293                     /* 10xx xxxx,  1111 xxxx */
294                     throw new UTFDataFormatException(
295                         "malformed input around byte " + count);
296             }
297         }
298         // The number of chars produced may be less than utflen
299         return new String(chararr, 0, chararr_count);
300     }
301 }
View Code

說明
DataInputStream 的作用就是“允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型。應用程序可以使用數據輸出流寫入稍后由數據輸入流讀取的數據。”
DataInputStream 中比較難以理解的函數就只有 readUTF(DataInput in);下面,對這個函數進行詳細的介紹,其它的函數請參考源碼中的注釋。

readUTF(DataInput in)源碼如下:

  1 public final static String readUTF(DataInput in) throws IOException {
  2     // 從“數據輸入流”中讀取“無符號的short類型”的值:
  3     // 注意:UTF-8輸入流的前2個字節是數據的長度
  4     int utflen = in.readUnsignedShort();
  5     byte[] bytearr = null;
  6     char[] chararr = null;
  7 
  8     // 如果in本身是“數據輸入流”,
  9     // 則,設置字節數組bytearr = "數據輸入流"的成員bytearr
 10     //     設置字符數組chararr = "數據輸入流"的成員chararr
 11     // 否則的話,新建數組bytearr和chararr
 12     if (in instanceof DataInputStream) {
 13         DataInputStream dis = (DataInputStream)in;
 14         if (dis.bytearr.length < utflen){
 15             dis.bytearr = new byte[utflen*2];
 16             dis.chararr = new char[utflen*2];
 17         }
 18         chararr = dis.chararr;
 19         bytearr = dis.bytearr;
 20     } else {
 21         bytearr = new byte[utflen];
 22         chararr = new char[utflen];
 23     }
 24 
 25     int c, char2, char3;
 26     int count = 0;
 27     int chararr_count=0;
 28 
 29     // 從“數據輸入流”中讀取數據並存儲到字節數組bytearr中;從bytearr的位置0開始存儲,存儲長度為utflen。
 30     // 注意,這里是存儲到字節數組!而且讀取的是全部的數據。
 31     in.readFully(bytearr, 0, utflen);
 32 
 33     // 將“字節數組bytearr”中的數據 拷貝到 “字符數組chararr”中
 34     // 注意:這里相當於“預處理的輸入流中單字節的符號”,因為UTF-8是1-4個字節可變的。
 35     while (count < utflen) {
 36         // 將每個字節轉換成int值
 37         c = (int) bytearr[count] & 0xff;
 38         // UTF-8的每個字節的值都不會超過127;所以,超過127,則退出。
 39         if (c > 127) break;
 40         count++;
 41         // 將c保存到“字符數組chararr”中
 42         chararr[chararr_count++]=(char)c;
 43     }
 44 
 45     // 處理完輸入流中單字節的符號之后,接下來我們繼續處理。
 46     while (count < utflen) {
 47         // 下面語句執行了2步操作。
 48         // (01) 將字節由 “byte類型” 轉換成 “int類型”。
 49         //      例如, “11001010” 轉換成int之后,是 “00000000 00000000 00000000 11001010”
 50         // (02) 將 “int類型” 的數據左移4位
 51         //      例如, “00000000 00000000 00000000 11001010” 左移4位之后,變成 “00000000 00000000 00000000 00001100”
 52         c = (int) bytearr[count] & 0xff;
 53         switch (c >> 4) {
 54             // 若 UTF-8 是單字節,即 bytearr[count] 對應是 “0xxxxxxx” 形式;
 55             // 則 bytearr[count] 對應的int類型的c的取值范圍是 0-7。
 56             case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
 57                 /* 0xxxxxxx*/
 58                 count++;
 59                 chararr[chararr_count++]=(char)c;
 60                 break;
 61 
 62             // 若 UTF-8 是雙字節,即 bytearr[count] 對應是 “110xxxxx  10xxxxxx” 形式中的第一個,即“110xxxxx”
 63             // 則 bytearr[count] 對應的int類型的c的取值范圍是 12-13。
 64             case 12: case 13:
 65                 /* 110x xxxx   10xx xxxx*/
 66                 count += 2;
 67                 if (count > utflen)
 68                     throw new UTFDataFormatException(
 69                         "malformed input: partial character at end");
 70                 char2 = (int) bytearr[count-1];
 71                 if ((char2 & 0xC0) != 0x80)
 72                     throw new UTFDataFormatException(
 73                         "malformed input around byte " + count);
 74                 chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
 75                                                 (char2 & 0x3F));
 76                 break;
 77 
 78             // 若 UTF-8 是三字節,即 bytearr[count] 對應是 “1110xxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“1110xxxx”
 79             // 則 bytearr[count] 對應的int類型的c的取值是14 。
 80             case 14:
 81                 /* 1110 xxxx  10xx xxxx  10xx xxxx */
 82                 count += 3;
 83                 if (count > utflen)
 84                     throw new UTFDataFormatException(
 85                         "malformed input: partial character at end");
 86                 char2 = (int) bytearr[count-2];
 87                 char3 = (int) bytearr[count-1];
 88                 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
 89                     throw new UTFDataFormatException(
 90                         "malformed input around byte " + (count-1));
 91                 chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
 92                                                 ((char2 & 0x3F) << 6)  |
 93                                                 ((char3 & 0x3F) << 0));
 94                 break;
 95 
 96             // 若 UTF-8 是四字節,即 bytearr[count] 對應是 “11110xxx 10xxxxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“11110xxx”
 97             // 則 bytearr[count] 對應的int類型的c的取值是15 
 98             default:
 99                 /* 10xx xxxx,  1111 xxxx */
100                 throw new UTFDataFormatException(
101                     "malformed input around byte " + count);
102         }
103     }
104     // The number of chars produced may be less than utflen
105     return new String(chararr, 0, chararr_count);
106 }

說明:

(01) readUTF()的作用,是從輸入流中讀取UTF-8編碼的數據,並以String字符串的形式返回。
(02) 知道了readUTF()的作用之后,下面開始介紹readUTF()的流程:

第1步,讀取出輸入流中的UTF-8數據的長度。代碼如下:

int utflen = in.readUnsignedShort();

UTF-8數據的長度包含在它的前兩個字節當中;我們通過readUnsignedShort()讀取出前兩個字節對應的正整數就是UTF-8數據的長度。

第2步,創建2個數組:字節數組bytearr 和 字符數組chararr。代碼如下:

 1 if (in instanceof DataInputStream) {
 2     DataInputStream dis = (DataInputStream)in;
 3     if (dis.bytearr.length < utflen){
 4         dis.bytearr = new byte[utflen*2];
 5         dis.chararr = new char[utflen*2];
 6     }
 7     chararr = dis.chararr;
 8     bytearr = dis.bytearr;
 9 } else {
10     bytearr = new byte[utflen];
11     chararr = new char[utflen];
12 }

首先,判斷該輸入流本身是不是DataInputStream,即數據輸入流;若是的話,
則,設置字節數組bytearr = "數據輸入流"的成員bytearr
      設置字符數組chararr = "數據輸入流"的成員chararr
否則的話,新建數組bytearr和chararr。

第3步,將UTF-8數據全部讀取到“字節數組bytearr”中。代碼如下:

in.readFully(bytearr, 0, utflen);

注意: 這里是存儲到字節數組,而不是字符數組!而且讀取的是全部的數據。

第4步,對UTF-8中的單字節數據進行預處理。代碼如下:

1 while (count < utflen) {
2     // 將每個字節轉換成int值
3     c = (int) bytearr[count] & 0xff;
4     // UTF-8的單字節數據的值都不會超過127;所以,超過127,則退出。
5     if (c > 127) break;
6     count++;
7     // 將c保存到“字符數組chararr”中
8     chararr[chararr_count++]=(char)c;
9 }

UTF-8的數據是變長的,可以是1-4個字節;在readUTF()中,我們最終是將全部的UTF-8數據保存到“字符數組(而不是字節數組)”中,再將其轉換為String字符串。
由於UTF-8的單字節和ASCII相同,所以這里就將它們進行預處理,直接保存到“字符數組chararr”中。對於其它的UTF-8數據,則在后面進行處理。

第5步,對“第4步 預處理”之后的數據,接着進行處理。代碼如下:

// 處理完輸入流中單字節的符號之后,接下來我們繼續處理。
while (count < utflen) {
    // 下面語句執行了2步操作。
    // (01) 將字節由 “byte類型” 轉換成 “int類型”。
    //      例如, “11001010” 轉換成int之后,是 “00000000 00000000 00000000 11001010”
    // (02) 將 “int類型” 的數據左移4位
    //      例如, “00000000 00000000 00000000 11001010” 左移4位之后,變成 “00000000 00000000 00000000 00001100”
    c = (int) bytearr[count] & 0xff;
    switch (c >> 4) {
        // 若 UTF-8 是單字節,即 bytearr[count] 對應是 “0xxxxxxx” 形式;
        // 則 bytearr[count] 對應的int類型的c的取值范圍是 0-7。
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
            /* 0xxxxxxx*/
            count++;
            chararr[chararr_count++]=(char)c;
            break;

        // 若 UTF-8 是雙字節,即 bytearr[count] 對應是 “110xxxxx  10xxxxxx” 形式中的第一個,即“110xxxxx”
        // 則 bytearr[count] 對應的int類型的c的取值范圍是 12-13。
        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;

        // 若 UTF-8 是三字節,即 bytearr[count] 對應是 “1110xxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“1110xxxx”
        // 則 bytearr[count] 對應的int類型的c的取值是14 。
        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;

        // 若 UTF-8 是四字節,即 bytearr[count] 對應是 “11110xxx 10xxxxxx  10xxxxxx  10xxxxxx” 形式中的第一個,即“11110xxx”
        // 則 bytearr[count] 對應的int類型的c的取值是15 
        default:
            /* 10xx xxxx,  1111 xxxx */
            throw new UTFDataFormatException(
                "malformed input around byte " + count);
    }
}

(a) 我們將下面的兩條語句一起進行說明

c = (int) bytearr[count] & 0xff;
switch (c >> 4) { ... }

首先,我們必須要理解 為什么要這么做(執行上面2條語句)呢?
原因很簡單,這么做的目的就是為了區分UTF-8數據是幾位的;因為UTF-8的數據是1~4字節不等。

我們先看看UTF-8在1~4位情況下的格式。

--------------------+---------------------------------------------
1字節 UTF-8的通用格式  | 0xxxxxxx
2字節 UTF-8的通用格式  | 110xxxxx 10xxxxxx
3字節 UTF-8的通用格式  | 1110xxxx 10xxxxxx 10xxxxxx
4字節 UTF-8的通用格式  | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

執行 c = (int) bytearr[count] & 0xff; 和 c>>4 這2項操作之后,上面的數據變成

--------------------+---------------------------------------------
1字節 UTF-8的變換后對應的int類型值  | 00000000 00000000 00000000 00000xxx    (范圍是0~7) 
2字節 UTF-8的變換后對應的int類型值  | 00000000 00000000 00000000 0000110x    (范圍是12~13) 
3字節 UTF-8的變換后對應的int類型值  | 00000000 00000000 00000000 00001110    (范圍是14) 
4字節 UTF-8的變換后對應的int類型值  | 00000000 00000000 00000000 00001111    (范圍是15) 

為什么會是這樣呢?
我們以“2字節 UTF-8的通用格式”來說明。
它的通用格式是 “110xxxxx 10xxxxxx”,我們在操作時,只會操作第1個字節,即只會操作“110xxxxx
(a.1) 在執行 c = (int) bytearr[count] & 0xff; 時,首先將 bytearr[count] 轉換成int。

“110xxxxx”

轉成int類型之后,變成

“11111111 11111111 11111111 110xxxxx”

因為“110xxxxx”是負數(第1為是1),所以轉換成int類型時多出來的位補1。

(a.2) 接着 c = (int) bytearr[count] & 0xff; 中,會將 “轉換成int類型后的bytearr[count]” 與 “0xff”進行 邏輯與(即&) 操作。結果如下:

“00000000 00000000 00000000 110xxxxx”

(a.3) 執行 c>>4 時,會將上面的結果左移4位。得到的結果如下:

“00000000 00000000 00000000 0000110x”

(b) 上面的理解之后,swicth (c>>4) { ... } 其中的省略號部分就相當容易理解了。
我們還是以“2字節 UTF-8的通用格式”來說明。
它會執行 case 12 和 case 13;源碼如下:

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));

(b.1) 由於這種情況對應的UTF-8數據是“2字節”的,因此,執行count+2;直接跳過2個字節。
(b.2) 由於chararr的元素是字符類型,而一個字符正好占2個字節;因為正好將(((c & 0x1F) << 6) | (char2 & 0x3F)); 的結果轉換成char,然后保存在chararr數組中。


第6步,將字符數組轉換成String字符串,並返回。代碼如下:

return new String(chararr, 0, chararr_count);

示例代碼

關於DataInputStream中API的詳細用法,參考示例代碼(DataInputStreamTest.java)

  1 import java.io.DataInputStream;
  2 import java.io.DataOutputStream;
  3 import java.io.ByteArrayInputStream;
  4 import java.io.File;
  5 import java.io.InputStream;
  6 import java.io.FileInputStream;
  7 import java.io.FileOutputStream;
  8 import java.io.IOException;
  9 import java.io.FileNotFoundException;
 10 import java.lang.SecurityException;
 11 
 12 /**
 13  * DataInputStream 和 DataOutputStream測試程序
 14  *
 15  * @author skywang
 16  */
 17 public class DataInputStreamTest {
 18 
 19     private static final int LEN = 5;
 20 
 21     public static void main(String[] args) {
 22         // 測試DataOutputStream,將數據寫入到輸出流中。
 23         testDataOutputStream() ;
 24         // 測試DataInputStream,從上面的輸出流結果中讀取數據。
 25         testDataInputStream() ;
 26     }
 27 
 28     /**
 29      * DataOutputStream的API測試函數
 30      */
 31     private static void testDataOutputStream() {
 32 
 33         try {
 34             File file = new File("file.txt");
 35             DataOutputStream out =
 36                   new DataOutputStream(
 37                       new FileOutputStream(file));
 38 
 39             out.writeBoolean(true);
 40             out.writeByte((byte)0x41);
 41             out.writeChar((char)0x4243);
 42             out.writeShort((short)0x4445);
 43             out.writeInt(0x12345678);
 44             out.writeLong(0x0FEDCBA987654321L);
 45 
 46             out.writeUTF("abcdefghijklmnopqrstuvwxyz嚴12");
 47 
 48             out.close();
 49        } catch (FileNotFoundException e) {
 50            e.printStackTrace();
 51        } catch (SecurityException e) {
 52            e.printStackTrace();
 53        } catch (IOException e) {
 54            e.printStackTrace();
 55        }
 56     }
 57     /**
 58      * DataInputStream的API測試函數
 59      */
 60     private static void testDataInputStream() {
 61 
 62         try {
 63             File file = new File("file.txt");
 64             DataInputStream in =
 65                   new DataInputStream(
 66                       new FileInputStream(file));
 67 
 68             System.out.printf("byteToHexString(0x8F):0x%s\n", byteToHexString((byte)0x8F));
 69             System.out.printf("charToHexString(0x8FCF):0x%s\n", charToHexString((char)0x8FCF));
 70 
 71             System.out.printf("readBoolean():%s\n", in.readBoolean());
 72             System.out.printf("readByte():0x%s\n", byteToHexString(in.readByte()));
 73             System.out.printf("readChar():0x%s\n", charToHexString(in.readChar()));
 74             System.out.printf("readShort():0x%s\n", shortToHexString(in.readShort()));
 75             System.out.printf("readInt():0x%s\n", Integer.toHexString(in.readInt()));
 76             System.out.printf("readLong():0x%s\n", Long.toHexString(in.readLong()));
 77             System.out.printf("readUTF():%s\n", in.readUTF());
 78 
 79             in.close();
 80        } catch (FileNotFoundException e) {
 81            e.printStackTrace();
 82        } catch (SecurityException e) {
 83            e.printStackTrace();
 84        } catch (IOException e) {
 85            e.printStackTrace();
 86        }
 87     }
 88 
 89     // 打印byte對應的16進制的字符串
 90     private static String byteToHexString(byte val) {
 91         return Integer.toHexString(val & 0xff);
 92     }
 93 
 94     // 打印char對應的16進制的字符串
 95     private static String charToHexString(char val) {
 96         return Integer.toHexString(val);
 97     }
 98 
 99     // 打印short對應的16進制的字符串
100     private static String shortToHexString(short val) {
101         return Integer.toHexString(val & 0xffff);
102     }
103 }

運行結果

byteToHexString(0x8F):0x8f
charToHexString(0x8FCF):0x8fcf
readBoolean():true
readByte():0x41
readChar():0x4243
readShort():0x4445
readInt():0x12345678
readLong():0xfedcba987654321
readUTF():abcdefghijklmnopqrstuvwxyz嚴12

結果說明
(01) 查看file.txt文本。16進制的數據顯示如下:


001f 對應的int值是31。它表示的含義是后面的UTF-8數據的長度。字符串“abcdefghijklmnopqrstuvwxyz嚴12”中字母“ab...xyz”的長度是26,“嚴”對應的UTF-8數據長度是3;“12”長度是2。總的長度=26+3+2=31。

(02) 返回byte對應的16進制的字符串
源碼如下:

private static String byteToHexString(byte val) {
    return Integer.toHexString(val & 0xff);
}

想想為什么代碼是:

return Integer.toHexString(val & 0xff);

而不是

return Integer.toHexString(val);

我們先看看 byteToHexString((byte)0x8F); 在上面兩種情況下的輸出結果。
return Integer.toHexString(val & 0xff); 對應的輸出是“0xffffff8f
return Integer.toHexString(val); 對應的輸出是“0x8f
為什么會這樣呢?
原因其實很簡單,就是“byte類型轉換成int類型”導致的問題。
byte類型的0x8F是一個負數,它對應的2進制是10001111;將一個負數的byte轉換成int類型時,執行的是有符號轉型(新增位都填充符號位的數字)。0x8F的符號位是1,因為將它轉換成int時,填充“1”;轉型后的結果(2進制)是11111111 11111111 11111111 10001111,對應的16進制為0xffffff8f。
因為當我們執行Integer.toHexString(val);時,返回的就是0xffffff8f。
在Integer.toHexString(val & 0xff)中,相當於0xffffff8f & 0xff,得到的結果是0x8f。

(03) 返回char和short對應的16進制的字符串
“返回char對應的16進制的字符串”對應的源碼如下:

private static String charToHexString(char val) {
    return Integer.toHexString(val);
}

“返回short對應的16進制的字符串”對應源碼如下:

private static String shortToHexString(short val) {
    return Integer.toHexString(val & 0xffff);
}

比較上面的兩個函數,為什么一個是 “val” ,而另一個是 “val & 0xffff”?
通過(02)的分析,我們類似的推出為什么 “返回short對應的16進制的字符串” 要執行“val & 0xffff”。
但是,為什么 “返回char對應的16進制的字符串” 要執行 “val” 即可。原因也很簡單,java中char是無符號類型,占兩個字節。將char轉換為int類型,執行的是無符號轉型,新增為都填充0。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM