1. 程式人生 > >Java IO系列3 位元組流之DataInputStream與DataOutputStream

Java IO系列3 位元組流之DataInputStream與DataOutputStream

編碼與字符集

什麼把這個專題放在這,因為DataInputStream與DataOutputStream裡有個
readUTF()或writeUTF(String str)方法。

由於這方面的內容我看的資料還有限,難免出錯,看到下面的內容後自己去找資料證實。不要相信別人的話,要自己求證。以下內容僅供參考

編碼和字符集不是一個概念,字符集表示碼點與字元之間的對映關係,至於怎麼儲存,字符集也不關心,具體儲存交給編碼,但是編碼要依賴於字符集。

ASCII碼

用一個位元組儲存字元,但是隻用了7位,即27 = 128個字元
ASCII表上的數字0–31分配給了控制字元,用於控制像印表機等一些外圍裝置。
數字 32–126 分配給了能在鍵盤上找到的字元,當您檢視或列印文件時就會出現。
數字127代表 DELETE 命令。
這裡寫圖片描述

擴充套件ASCII

擴充套件的ASCII字元滿足了對更多字元的需求。擴充套件的ASCII包含ASCII中已有的128個字元(數字0–32顯示在下圖中),又增加了128個字元,總共是256個。即使有了這些更多的字元,許多語言還是包含無法壓縮到256個字元中的符號。因此,出現了一些ASCII的變體來囊括地區性字元和符號。例如,許多軟體程式把ASCII表(又稱作ISO8859-1)用於北美、西歐、澳大利亞和非洲的語言。
這裡寫圖片描述

因為ASCII就一套,ASCII字符集也可以叫ASCII編碼

Unicode字符集及其以Unicode字符集的編碼

Unicode字符集下有多個編碼方案UTF-8、UTF-16、UTF-32.

1.Unicode並不涉及字元是怎麼在位元組中表示的,它僅僅指定了字元對應的數字
2.Unicode只是一個用來對映字元和數字的標準。它對支援字元的數量沒有限制,也不要求字元必須佔兩個、三個或者其它任意數量的位元組。
3.目前Unicode碼點的範圍是U+0000~U+10FFFF。U+10FFFF是多大呢?大概是111萬,按Unicode官方的說法,就這樣了,以後也不擴充了,一百多萬足夠用了,目前也只是定義了10萬多個字元左右

Unicode的範圍目前是U+0000~U+10FFFF,理論大小為10FFFF+1=11000016。後一個1代表是65536,因為是16進位制,所以前一個1是後一個1的16倍,所以總共有1×16+1=17個的65536的大小,粗略估算為17×6萬=102萬,所以這是一個百萬級別的數。
為了更好分類管理如此龐大的碼點數,把每65536個碼點作為一個平面,總共17個平面。

平面,BMP,SP

碼點的全部範圍可以均分成17個65536大小的部分,這裡面的每一個部分就是一個平面(Plane)。編號從0開始,第一個平面稱為Plane 0.

這裡寫圖片描述

第一個平面即是BMP(Basic Multilingual Plane 基本多語言平面),也叫Plane 0,它的碼點範圍是U+0000~U+FFFF。這也是我們最常用的平面,日常用到的字元絕大多數都落在這個平面內。
UTF-16只需要用兩位元組編碼此平面內的字元。
後續的16個平面稱為SP(Supplementary Planes,增補平面)。顯然,這些碼點已經是超過U+FFFF的了,所以已經超過了16位空間的理論上限,對於這些平面內的字元,UTF-16採用了四位元組編碼。

其中很多平面還是空的,還沒有分配任何字元,只是先規劃了這麼多。
另:有些還屬於私有的,如上圖中的最後兩個Private Use Planes,在此可自定義字元。

正則表示式[\u4E00-\u9FA5]來匹配中文位置,嚴格來說這只是Unicode最主要的一段中文區域。
有的中文也落在了增補平面內。

代理區

這裡寫圖片描述

BMP縮圖中有一片空白,這就是所謂的代理區(Surrogate Area)
可以看到這段空白從D8~DF。其中D800–DBFF屬於高代理區(High Surrogate Area),後面的DC00–DFFF屬於低代理區(Low Surrogate Area),各自的大小均為4×256=1024。
還可以看到在它之前是韓文的區域,之後E0開始到F8的則是屬於私有的(private),可以在這裡定義自己專用的字元。

至此我們對Unicode的碼點,平面都有了一定的瞭解,但我們還沒有觸及一個重要的方面,那就是碼點到最終編碼的轉換,在Unicode中,這稱為UTF。

UTF-32

我們說碼點最大的10FFFF也就21位,而UTF-32採用的定長四位元組則是32位,所以它表示所有的碼點不但毫無壓力,反而綽綽有餘,所以只要把碼點的表示形式以前補0的形式補夠32位即可。這種表示的最大缺點是佔用空間太大。

再來看稍複雜一點的UTF-8。

UTF-8

UTF-8是變長的編碼方案,可以有1,2,3,4四種位元組組合。在前面的定長與變長篇章我們提到UTF-8採用了高位保留方式來區別不同變長,如下:
這裡寫圖片描述
碼點與位元組如何對應?

哪些碼點用哪種變長呢?可以先把碼點變成二進位制,看它有多少有效位(去掉前導0)就可以確定了。

  1. 一位元組有效編碼位有7位,27=128,碼點U+0000~U+007F(0~127)使用一位元組。

一位元組留給了ASCII,所以UTF-8相容ASCII。

  1. 二位元組有效編碼位只有5+6=11位,最多隻有211=2048個編碼空間,所以數量眾多的漢字是無法容身於此的了。碼點U+0080~U+07FF(128~2047)使用二位元組。

注意:這裡碼點從128~2047,因為去掉了一位元組的碼點,所以不會佔滿2048個編碼空間,是有冗餘的,但你不能把適用於一位元組的碼點放到這裡來編碼。下同。

  1. 三位元組模式可看到光是保留位就達到4+2+2=8位,相當一位元組,所以只剩下兩位元組16位有效編碼位,它的容量實際也只有65536。碼點U+0800~U+FFFF(2048~65535)使用三位元組編碼。

一些漢字字典收錄的漢字達到了驚人的10萬級別。基本上,常用的漢字都落在了這三位元組的空間裡,這就是我們常說的漢字在UTF-8裡用三位元組表示。當然了,這麼說並不嚴謹,如果這10萬的漢字都被收錄進來的話,那些偏門的漢字自然只能被擠到四位元組空間上去了。

  1. 四位元組的可以看到它的有效位是3+6+6+6=21位,前面說到最大的碼點10FFFF也是21位,U+FFFF以上的增補平面的字元都在這裡來表示。

UTF-16

UTF-16是一種變長的2或4位元組編碼模式。對於BMP內的字元使用2位元組編碼,其它的則使用4位元組組成所謂的代理對來編碼。

代理區是UTF-16為了編碼增補平面中的字元而保留的,總共有2048個位置,均分為高代理區(D800–DBFF)和低代理區(DC00–DFFF)兩部分,各1024,這兩個區組成一個二維的表格,共有1024×1024=210×210=24×216=16×65536,所以它恰好可以表示增補的16個平面中的所有字元。
下面的圖片來自wiki
這裡寫圖片描述
下圖來自http://rishida.net/docs/unicode-tutorial/part2
這裡寫圖片描述

FilterOutputStream與FilterInputStream

這兩個都分別是InputStream和OutputStream的子類。也是DataInputStream與DataOutputStream的父類,而且,FilterInputStream和FilterOutputStream是具體的子類,實現了InputStream和OutputStream這兩個抽象類中為給出實現的方法。

但是,FilterInputStream和FilterOutputStream僅僅是“裝飾者模式”封裝的開始,它們在各個方法中的實現都是最基本的實現,都是基於構造方法中傳入引數封裝的InputStream和OutputStream的原始物件。

比如,在FilterInputStream類中,封裝了這樣一個屬性

protected volatile InputStream in;

而對應的構造方法是:

protected FilterInputStream(InputStream in) {
    this.in = in;
}

read()方法的實現則為:

public int read() throws IOException {
    return in.read();
}

其它方法的實現,以及FilterOutputStream也都是同理類似的。
我們注意到FilterInputStream和FilterOutputStream並沒給出其它額外的功能實現,只是做了一層簡單地封裝。那麼實現額外功能的實際是FilterInputStream和FilterOutputStream的各個子類。

DataInputStream與DataOutputStream

這也是比較重要的一對Filter實現。那麼說起功能,實際上就不得不提到他們除了extends FilterInputStream/FilterOutputStream外,還額外實現了DataInput和DataOutput介面。

我們可以先來看下DataInput和DataOutput這兩個interface。
這裡寫圖片描述 這裡寫圖片描述

而DataInputStream/DataOutputStream這一對實際上所做的也就是這兩個介面所定義的方法。再DataInputStream/DataOutputStream中,這些方法做了拼接和拆分位元組的工作。通過這些方法,我們可以方便的讀取、寫出各種我們實際所面對的型別的資料,而不必具體去在位元組層面上做細節操作。

public class DataInputStream extends FilterInputStream implements DataInput

public
class DataInputStream extends FilterInputStream implements DataInput {

    //in:資料來源
    public DataInputStream(InputStream in) {
        super(in);
    }

    //位元組陣列,80位元組,當我們readUTF時,先不解碼,直接把資料存進bytearr裡,若果超過80,重新new一個更大的
    private byte bytearr[] = new byte[80];
    //上面的是原始資料位元組,chararr是經過解碼後的資料,若果超過80,重新new一個更大的
    private char chararr[] = new char[80];

    //把資料讀進b[]數組裡,迴圈讀取,一個一個位元組的讀
    public final int read(byte b[]) throws IOException {
        return in.read(b, 0, b.length);
    }

    //把資料從b[]陣列的off位置讀進len個長度的位元組,迴圈讀取,一個一個位元組的讀
    public final int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }

   //一次讀取最多b.length個位元組到b[]數組裡
    public final void readFully(byte b[]) throws IOException {
        readFully(b, 0, b.length);
    }

    //一次讀取最多len個位元組到b[]數組裡,偏移值off
    //該方法與read(byte b[], int off, int len)簽名很像,但是read是一個一位元組(可能會出現迴圈)的讀,readFully是整個資料塊讀
    public final void readFully(byte b[], int off, int len) throws IOException {
        if (len < 0)
            throw new IndexOutOfBoundsException();
        int n = 0;
        while (n < len) {
            int count = in.read(b, off + n, len - n);
            if (count < 0)
                throw new EOFException();
            n += count;
        }
    }

    //跳過多少位元組,返回值是實際跳過多少位元組取值範圍[0,n]
    public final int skipBytes(int n) throws IOException {
        int total = 0;
        int cur = 0;

        //
        while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
            total += cur;
        }

        return total;
    }

   //讀取一個boolean值,佔據一個位元組
    public final boolean readBoolean() throws IOException {
        int ch = in.read();
        if (ch < 0)
            throw new EOFException();
        return (ch != 0);
    }

    //讀取一個位元組
    public final byte readByte() throws IOException {
        int ch = in.read();
        if (ch < 0)
            throw new EOFException();
        return (byte)(ch);
    }

    //讀取一個無符號的位元組
    public final int readUnsignedByte() throws IOException {
        int ch = in.read();
        if (ch < 0)
            throw new EOFException();
        return ch;
    }

    //讀取一個short型 2個位元組,分2步讀取一個位元組,然後把這2個位元組合併,強轉short
    public final short readShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (short)((ch1 << 8) + (ch2 << 0));
    }

    //讀取一個無符號short型 2個位元組,分2步讀取一個位元組,然後把這2個位元組合併
    public final int readUnsignedShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (ch1 << 8) + (ch2 << 0);
    }

    //讀取一個char ,2個位元組,然後把這2個位元組合併,強轉char
    public final char readChar() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (char)((ch1 << 8) + (ch2 << 0));
    }

    //讀取一個int ,4位元組,然後把這4個位元組合併
    public final int readInt() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        int ch3 = in.read();
        int ch4 = in.read();
        if ((ch1 | ch2 | ch3 | ch4) < 0)
            throw new EOFException();
        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
    }

    //readLong(),readDouble() 時的緩衝區
    private byte readBuffer[] = new byte[8];

    //一次性讀取一個long型值 ,8位元組,然後把這8個位元組合併
    public final long readLong() throws IOException {
        readFully(readBuffer, 0, 8);
        return (((long)readBuffer[0] << 56) +
                ((long)(readBuffer[1] & 255) << 48) +
                ((long)(readBuffer[2] & 255) << 40) +
                ((long)(readBuffer[3] & 255) << 32) +
                ((long)(readBuffer[4] & 255) << 24) +
                ((readBuffer[5] & 255) << 16) +
                ((readBuffer[6] & 255) <<  8) +
                ((readBuffer[7] & 255) <<  0));
    }

    //讀取一個float型的值,4位元組
    public final float readFloat() throws IOException {
        return Float.intBitsToFloat(readInt());
    }

  //讀取一個double型的值,8位元組
    public final double readDouble() throws IOException {
        return Double.longBitsToDouble(readLong());
    }

    private char lineBuffer[];

    @Deprecated
    public final String readLine() throws IOException {
        char buf[] = lineBuffer;

        if (buf == null) {
            buf = lineBuffer = new char[128];
        }

        int room = buf.length;
        int offset = 0;
        int c;

loop:   while (true) {
            switch (c = in.read()) {
              case -1:
              case '\n':
                break loop;

              case '\r':
                int c2 = in.read();
                if ((c2 != '\n') && (c2 != -1)) {
                    if (!(in instanceof PushbackInputStream)) {
                        this.in = new PushbackInputStream(in);
                    }
                    ((PushbackInputStream)in).unread(c2);
                }
                break loop;

              default:
                if (--room < 0) {
                    buf = new char[offset + 128];
                    room = buf.length - offset - 1;
                    System.arraycopy(lineBuffer, 0, buf, 0, offset);
                    lineBuffer = buf;
                }
                buf[offset++] = (char) c;
                break;
            }
        }
        if ((c == -1) && (offset == 0)) {
            return null;
        }
        return String.copyValueOf(buf, 0, offset);
    }

    public final String readUTF() throws IOException {
        return readUTF(this);
    }
    public final static String readUTF(DataInput in) throws IOException {
        //先讀取2位元組的utf的長度資訊 2^16-1 = 65535
        //因為writeUTF的時候長度不許>=64K(即65535個位元組)
        int utflen = in.readUnsignedShort();
        byte[] bytearr = null;
        char[] chararr = null;

        // 如果in本身是“資料輸入流”,  
        // 則,設定位元組陣列bytearr = "資料輸入流"的成員bytearr  
        //     設定字元陣列chararr = "資料輸入流"的成員chararr  
        // 否則的話,新建陣列bytearr和chararr  
        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;
        // 從“資料輸入流”中讀取資料並存儲到位元組陣列bytearr中;從bytearr的位置0開始儲存,儲存長度為utflen。  
        // 注意,這裡是儲存到位元組陣列!而且讀取的是全部的資料。  
        in.readFully(bytearr, 0, utflen);

        //如果是ascii碼就直接存在bytearr裡面了,畢竟老外寫的原始碼,都是用ascii碼機率比較高,就省去下面for中的判斷了
        // 將“位元組陣列bytearr”中的資料 拷貝到 “字元陣列chararr”中  
        // 注意:這裡相當於“預處理的輸入流中單位元組的符號”,因為UTF-8是1-4個位元組可變的。
        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            // UTF-8的單位元組資料的值都不會超過127;所以,超過127,則退出。
            if (c > 127) break;
            count++;
            chararr[chararr_count++]=(char)c;
        }

        // 處理完輸入流中單位元組的符號之後,接下來我們繼續處理。  
        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) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    /* 0xxxxxxx*/
                    // 若 UTF-8 是單位元組,即 bytearr[count] 對應是 “0xxxxxxx” 形式;  
                    // 則 bytearr[count] 對應的int型別的c的取值範圍是 0-7。  
                    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);
            }
        }
        // The number of chars produced may be less than utflen
        return new String(chararr, 0, chararr_count);
    }
}

public class DataOutputStream extends FilterOutputStream implements DataInput

public
class DataOutputStream extends FilterOutputStream implements DataOutput {
    /**
     * The number of bytes written to the data output stream so far.
     * If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
     */
    protected int written;

    /**
     * bytearr is initialized on demand by writeUTF
     */
    private byte[] bytearr = null;

    public DataOutputStream(OutputStream out) {
        super(out);
    }

    private void incCount(int value) {
        int temp = written + value;
        if (temp < 0) {
            temp = Integer.MAX_VALUE;
        }
        written = temp;
    }

    public synchronized void write(int b) throws IOException {
        out.write(b);
        incCount(1);
    }

    public synchronized void write(byte b[], int off, int len)
        throws IOException
    {
        out.write(b, off, len);
        incCount(len);
    }

    public void flush() throws IOException {
        out.flush();
    }

    public final void writeBoolean(boolean v) throws IOException {
        out.write(v ? 1 : 0);
        incCount(1);
    }

    public final void writeByte(int v) throws IOException {
        out.write(v);
        incCount(1);
    }

    public final void writeShort(int v) throws IOException {
        out.write((v >>> 8) & 0xFF);
        out.write((v >>> 0) & 0xFF);
        incCount(2);
    }

    public final void writeChar(int v) throws IOException {
        out.write((v >>> 8) & 0xFF);
        out.write((v >>> 0) & 0xFF);
        incCount(2);
    }

    public final void writeInt(int v) throws IOException {
        out.write((v >>> 24) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>>  8) & 0xFF);
        out.write((v >>>  0) & 0xFF);
        incCount(4);
    }

    private byte writeBuffer[] = new byte[8];

    public final void writeLong(long v) throws IOException {
        writeBuffer[0] = (byte)(v >>> 56);
        writeBuffer[1] = (byte)(v >>> 48);
        writeBuffer[2] = (byte)(v >>> 40);
        writeBuffer[3] = (byte)(v >>> 32);
        writeBuffer[4] = (byte)(v >>> 24);
        writeBuffer[5] = (byte)(v >>> 16);
        writeBuffer[6] = (byte)(v >>>  8);
        writeBuffer[7] = (byte)(v >>>  0);
        out.write(writeBuffer, 0, 8);
        incCount(8);
    }

    public final void writeFloat(float v) throws IOException {
        writeInt(Float.floatToIntBits(v));
    }

    public final void writeDouble(double v) throws IOException {
        writeLong(Double.doubleToLongBits(v));
    }
    public final void writeBytes(String s) throws IOException {
        int len = s.length();
        for (int i = 0 ; i < len ; i++) {
            out.write((byte)s.charAt(i));
        }
        incCount(len);
    }

    public final void writeChars(String s) throws IOException {
        int len = s.length();
        for (int i = 0 ; i < len ; i++) {
            int v = s.charAt(i);
            out.write((v >>> 8) & 0xFF);
            out.write((v >>> 0) & 0xFF);
        }
        incCount(len * 2);
    }

    public final void writeUTF(String str) throws IOException {
        writeUTF(str, this);
    }

    static int writeUTF(String str, DataOutput out) throws IOException {
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        /* use charAt instead of copying String to char array */
        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");

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

        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)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }
        out.write(bytearr, 0, utflen+2);
        return utflen + 2;
    }

    public final int size() {
        return written;
    }
}

相關推薦

Java IO系列3 位元組DataInputStreamDataOutputStream

編碼與字符集 什麼把這個專題放在這,因為DataInputStream與DataOutputStream裡有個 readUTF()或writeUTF(String str)方法。 由於這方面的內容我看的資料還有限,難免出錯,看到下面的內容後自己去找資

Java IO系列4 位元組PushbackInputStream

PushbackInputStream 原理是在內部儲存一個位元組緩衝區,如果unRead一個位元組,就會向這個緩衝區倒著寫入資料,下一次read的時候,就會把該緩衝區裡的位元組讀取出來。 當緩衝區裡沒資料時,跟別的位元組輸入流一樣 但是當有資料時

Java IO 筆記 3 --- 物件

如果想整個的存入,讀取,自定義的物件,就用到了,操作物件的流 — ObjectOutputStream, ObjectInputStream,被操作的物件,要實現 Serializable(標記介面) 注:流裡面的一對,不是兩個,是輸入和輸出相對應

java io系列14:DataInputStream(資料輸入)的認知、原始碼和示例

本章介紹DataInputStream。我們先對DataInputStream有個大致認識,然後再深入學習它的原始碼,最後通過示例加深對它的瞭解。 轉載請註明出處:http://www.cnblogs.com/skywang12345/p/io_14.html DataInputStream

Java IO 3-位元組字元的區別

java以流的形式操縱IO,又將流分為兩種:位元組流與字元流,對JavaIO流操作不清楚的可以參考 那位元組流與字元流之間又有什麼區別呢 區別 位元組流用於操作檔案資料,網路傳輸等場景,而字元流適用於與字串,中文有關的內容處理,因為Writer/R

系統學習 Java IO (十二)----資料和物件 DataInputStream/DataOutputStream & ObjectInputStream/ObjectOutputStream

DataInputStream/DataOutputStream 允許應用程式以與機器無關方式從底層輸入流中讀取基本 Java 資料型別。 要想使用資料輸出流和輸入流,必須按指定的格式儲存資料,才可以將資料輸入流將資料讀取進來,所以通常使用 DataInputStream 來讀取 DataOutputStr

Java io FileOutputStreamFileInputStream 詳解

FileOutputStream 檔案輸出流 方法程式碼詳解: public class Demo01 { public static void main(String[] a

Java IO系列初始IO

什麼是IO流?        IO指的是輸入/輸出(Input/Output),而流是一種抽象概念,這裡我們可以把流想像成是一條長河,在上游有一水庫提供水源,河邊住著一戶人,隨時都可以從河邊取到水,

IO相關3(string)

nes for val 的人 有用 ugo oid 支持 struct sstream 頭文件定義了三個類型來支持內存 IO,這些類型可以向 string 寫入數據,從 string 讀取數據,就像 string 是一個 IO 流一樣。 istringstream 從 st

Java——IO類,轉換簡化寫法

lose exception rip border 兩個類 xhtml 成員方法 tab 指示 body, table{font-family: 微軟雅黑} table{border-collapse:

系統學習 Java IO (十)----回退 PushbackInputStream

目錄:系統學習 Java IO---- 目錄,概覽 PushbackInputStream 旨在從 InputStream 解析資料時使用。 有時您需要先讀取幾個位元組以檢視將要發生的事情,然後才能確定如何解釋當前位元組, PushbackInputStream 允許這樣做。 實際上,它允許將讀取的位元組推

系統學習 Java IO (十一)----列印 PrintStream

目錄:系統學習 Java IO---- 目錄,概覽 PrintStream 類可以將格式化資料寫入底層 OutputStream 。 PrintStream 類可以格式化基本型別,如int,long等格式化為文字,而不是其位元組值。 這就是為什麼它被稱為 PrintStream ,因為它將原始位元組格式化為

nested exception is com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 位元組

maven切換資料庫連線配置時出現 nested exception is com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 位元組的 UTF-8 序列的位元組 3 無效。

com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 位元組的 UTF-8 序列的位元組 3 無效。

在tomcat啟動時報錯,核心錯誤 org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from URL [jar:file:

com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 位元組的 UTF-8 序列的位元組 x 無效

在啟動Tomcat專案時,控制檯報錯:nested exception is com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 位元組的 UTF-8 序列的位元組 2 無效。 該錯誤是由於一些配置檔案如:

Java IO詳解(六)------序列化反序列化(物件

1、什麼是序列化與反序列化? 序列化:指把堆記憶體中的 Java 物件資料,通過某種方式把物件儲存到磁碟檔案中或者傳遞給其他網路節點(在網路上傳輸)。這個過程稱為序列化。通俗來說就是將資料結構或物件轉換成二進位制串的過程   反序列化:把磁碟檔案中

Java——I/O(位元組、字元轉換

位元組流和字元流 位元組流(byte):InputStream、OutputStream  字元流(char):Reader、Writer 位元組流與字元流的區別: 位元組流是原生的操作,而字元流是經過處理後的操作。 一般使用位元組流——無論是網路傳

Java併發系列 | AbstractQueuedSynchronizer原始碼分析概要分析

學習Java併發程式設計不得不去了解一下java.util.concurrent這個包,這個包下面有許多我們經常用到的併發工具類,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等。而這些類的底層實現都依賴於AbstractQueu

Java I/O 使用位元組/字元進行檔案拷貝

例項 利用位元組流複製檔案 /** * 利用位元組流複製檔案 * @throws IOException */ @Test public void testByteCopy() throws IOException { InputStrea

JAVA io筆記10 列印

package FileText; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; impor