java IO(三):字元流

来源:https://www.cnblogs.com/f-ck-need-u/archive/2017/12/30/8151258.html
-Advertisement-
Play Games

字元流按字元個數輸入、輸出數據。 1.Reader類和FileReader類 Reader類是字元輸入流的超類,FileReader類是讀取字元的便捷類,此處的便捷是相對於其父類(另一個字元輸入流)InputStreamReader而言的。 read()每單字元讀取: read(char[] c)讀 ...


字元流按字元個數輸入、輸出數據。

1.Reader類和FileReader類

Reader類是字元輸入流的超類,FileReader類是讀取字元的便捷類,此處的便捷是相對於其父類(另一個字元輸入流)InputStreamReader而言的。

read()每單字元讀取:

import java.io.*;


public class FileR {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:/temp/hello.txt");

        int ch = 0;
        while((ch=fr.read())!=-1) {
            System.out.println((char)ch);
        }

        fr.close();
    }
}

read(char[] c)讀取字元緩衝到字元數組:

import java.io.*;

public class FileR {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:/temp/hello.txt");

        int len = 0;
        char[] buf = new char[1024];
        while ((len=fr.read(buf))!=-1) {
            System.out.println(new String(buf,0,len));  //字元數組轉換為字元串輸出
        }

        fr.close();
    }
}

2.Writer類和FileWriter類

Writer類是字元輸出流的超類,FileWriter類是輸出字元的便捷類,此處的便捷是相對於其父類(另一個字元輸出流)InputStreamWriter而言的。

import java.io.*;

public class FileW {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("d:/temp/hellow.txt");

        fw.write("a你好,謝謝,再見!");
        fw.flush();   //儘量每write一次就flush一次
        fw.close();
    }
}

flush()和close()的註意點:

(1).close()自帶flush(),它在關閉流之前會自動先flush()一次。
(2).flush()後流還能繼續使用,而close()後流就被關閉不可再被使用。
(3).為了防止數據丟失,應儘量每write一次就flush一次。但最後一次可不用flush(),因為close()自帶flush()。

3.InputStreamReader類和OutputStreamWriter類

FileReader和FileWriter分別是InputStreamReader和OutputStreamWriter的便捷類,便捷類的意思是前兩個類可以簡化後兩個類,同時又能達到相同的目的。實際上,FileReader和FileWriter是InputStreamReader和OutputStreamWriter的子類,等價於它們的預設形式。

InputStreamReader、OutputStreamWriter可以看作是位元組流和字元流之間的橋梁。前者將字元按照字元集轉換為位元組(二進位格式)並讀取字元,這稱為編碼(例如:a->97(01100001));後者將位元組(二進位格式)按照字元集轉換為字元並寫入字元,這稱為解碼(例如:97(01100001)->a)

InputStreamReader、OutputStreamWriter的預設字元集採用的是操作系統的字元集,對於簡體中文的Windows系統,預設採用的是GBK字元集。

以下是OutputStreamWriter以utf-8編碼格式寫入字元的示例:

import java.io.*;

public class OutputStreamW {
    public static void main(String[] args) throws IOException {
        WriteCN();
    }

    public static void WriteCN() throws IOException {
        OutputStreamWriter osw = 
                new OutputStreamWriter(new FileOutputStream("d:/temp/hellow.txt"),"utf-8");
              //new OutputStreamWriter(new FileOutputStream("d:/temp/hellow.txt"));//採用預設gbk字元集寫入
        osw.write("a你好,謝謝,再見!!!");
        osw.flush();
        osw.close();
    }
}

以下是InputStreamReader讀取上述文件d:\temp\hellow.txt中字元的示例,因為hellow.txt中的字元編碼為UTF-8,因此讀取時必須也也utf-8讀取。假如以預設的gbk字元集讀取,由於每次讀取2個位元組,將會把utf-8字元(中文字元占用3個位元組)切分開導致亂碼:

import java.io.*;

public class InputStreamR {
    public static void main(String[] args) throws IOException {
        ReadCN();
    }

    public static void ReadCN() throws IOException {
        InputStreamReader isr = 
                new InputStreamReader(new FileInputStream("d:/temp/hellow.txt"),"utf-8");
        //      new InputStreamReader(new FileInputStream("d:/temp/hellow.txt"));//預設字元集,亂碼

/*         int ch = 0;
        while ((ch=isr.read())!=-1) {
            System.out.println((char)ch);
        } */

        int len = 0;
        char[] buf = new char[1024];
        while ((len=isr.read(buf))!=-1) {
            System.out.println(new String(buf,0,len));
        }
        isr.close();
    }
}

4.位元組流、字元流的關係(編碼、解碼、編碼表(字元集))

首先說明編碼、解碼和編碼表(字元集)的關係。

編碼:將字元根據編碼表轉換為二進位的0/1數字。
解碼:將二進位根據編碼表轉換為字元。
編碼表:

  1.ascii碼表:使用一個位元組中的7位二進位就能表示字母、數字、英文標點等字元。
  2.iso8859-1碼表:(也即latin-1),使用一個位元組中的8位二進位,其內包含了ascii碼表中的字元,此外還擴展了歐洲一些語言的字元。
  3.GB2312:中國自編的編碼表,2個位元組,兩個位元組都是負數,收錄了6700多個漢字。相容ascii。
   GBK:K代表擴展的意思,是GB2312的擴展,占用2位元組,大部分兩個位元組都是負數,但少數幾個字元的第二個位元組是正數,其內包含了GB2312的碼表,收錄了2萬多個漢字。
   GB18030:新的編碼表,變長(可能1、2、4位元組),其內包含了GB2312和GBK的碼表,收錄了7萬多個漢字。
  4.Unicode:收錄了全世界幾乎所有的字元,但無論什麼字元都占用2個位元組,不足兩個位元組的0占位。
  5.UTF-8:解決了unicode的缺點,它使用變長1-4位元組來表示一個字元(空間能省則省),其中ascii部分仍占用1個位元組(仍相容ascii)。和Unicode不同的是,UTF-8中的中文字元占用3個位元組。

因此,需要知道的是:

  • (1)所有字元集都相容ascii碼表;
  • (2)一般考慮字元集時,需要考慮的碼表大致分:ascii、latin-1、GBK、utf-8。
  • (3)不同碼表之間,因為編碼、解碼規則不一樣,會導致文本亂碼問題。
  • (4)GBK不會和ascii衝突。因為GBK即使有正數的位元組,也一定是在第二個位元組。因為解碼時,如果讀取的第一個位元組為正數,則一定是ascii字元,如果讀取的第一個位元組為負數,則一定是中文字元,此時會繼續讀取下一個位元組。
  • (5)以上都是文本的編碼、解碼。除了文本,還有媒體類數據,它們都有各自的編碼、解碼規則。實際使用過程中,採用什麼方式編碼、解碼,取決於打開文件的程式,例如不能用記事本類程式打開媒體類(圖片、音頻、視頻等)文件,不能用視頻播放器打開文本文件。

再來說明字元流和位元組流的關係,也就是實現字元流的原理。

對於字元串"abcde",使用位元組流能夠很輕鬆地讀取、寫入,但對於字元串"a你好,謝謝,再見!"這樣的中文字元(假設它們是gbk編碼的),無論是採用位元組流的單位元組讀取還是以位元組數組讀取多個位元組的方式都很難實現讀取、寫入。例如,一次讀取2個位元組,則第一次讀取的兩個位元組為a和"你"的前一個位元組,a的ascii碼為97,"你"的前一個位元組也是一個數值,假如為196,gbk對196也有對應的編碼,假設對應的字元為"浣",於是第一次的兩個位元組經過解碼,得到"a浣",而非"a你",這已經亂碼了。而且很多時候,編碼表中有某些數值並沒有對應的字元,這時看到的就是亂七八糟的符號。

如果採用字元流讀取、寫入字元,則會先將其轉換為位元組數組,再對位元組數組中的位元組進行編碼、解碼。也就是說,字元流的底層還是位元組流。而且根據不同的編碼表(字元集),轉換為位元組存儲到位元組數組中時,數值和占用位元組數也是不一樣的。

以InputStreamReader和OutputStreamWriter這兩個字元流按照utf-8字元集解析"你好"兩個字元為例。

5.InputStreamReader/OutputStreamWriter和FileReader/FileWriter的區別

FileReader/FileWriter類是InputStreamReader/OutputStreamWriter類的子類,它們都能處理字元。區別是前者是後者的便捷類,是後者的預設形式。

也就是說,當InputStreamReader/OutputStreamWriter採用預設字元集時,它們和FileReader/FileWriter是等價的。即以下三條代碼是等價的。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));   //預設字元集
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");  //指定為預設字元集
FileReader fr = new FileReader("a.txt");   //使用便捷類FileReader

所以,如果採用的是預設字元集,最佳方式是採用FileReader/FileWriter,但如果需要指定字元集,則必須使用InputStreamReader/OutputStreamWriter。

6.字元流複製文本類文件

import java.io.*;

public class CopyFileByChar {
    public static void main(String[] args) throws IOException {
        copy("d:/temp/big.log","d:/temp/big_bak.log");
    }

    public static void copy(String src,String dest) throws IOException {
        //字元集必須設置正確
        InputStreamReader isr = new InputStreamReader(new FileInputStream(src),"gbk");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(dest),"gbk");

        int len = 0;
        char[] buf = new char[1024];
        while((len=isr.read(buf))!=-1) {
            osw.write(buf,0,len);
            osw.flush();
        }

        osw.close();
        isr.close();
    }
}

7.操作行:BufferedReader類BufferedWriter類

它們是緩衝類字元流,分別用於創建帶有輸入、輸出緩衝區的輸入、輸出字元流。

其實和字元數組的作用差不多,只不過設計為緩衝類後可以使用類的一些方法,最常用的是操作行的方法:

  • BufferedReader的readLine()方法:讀取一行數據。只要遇到換行符(\n)、回車符(\r)都認為一行結束。當讀取到流末尾時返回null。
  • BufferedWriter的newLine()方法:寫入一個換行符。

例如,下麵是用這兩個類來複制文本文件d:\temp\big.log。

import java.io.*;


public class CopyByBuffer {
    public static void main(String[] args) throws IOException {
        copy("d:/temp/big.log","d:/temp/big_bak.log");
    }

    public static void copy(String src,String dest) throws IOException {

        // src buffer & dest buffer
        InputStreamReader isr = new InputStreamReader(new FileInputStream(src),"gbk");
        BufferedReader bufr = new BufferedReader(isr);

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(dest),"gbk");
        BufferedWriter bufw = new BufferedWriter(osw);

        String line = null;
        while((line=bufr.readLine())!=null) {
            bufw.write(line);
            bufw.newLine();  
            bufw.flush();
        }

        bufw.close();
        bufr.close();
    }
}

對於大文件來說,這BufferedReader類操作數據其實比字元數組的速度要慢,因為每次緩衝一行,一行才不到1k而已(除非行很長),而char[]通常都設置為好幾k,一次就能讀很多行。

 

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡單工廠又稱為靜態工廠方法(static factory method)模式,簡單工廠是由一個工廠來決定創建出哪一種個體的實現,在很多的討論中,簡單工廠做為工廠方法模式(Factory Method)的一個特殊案例出現. 這個模式封裝的變化點是什麼? 這是每一個模式都應該考慮的核心問題,一定要記得, ...
  • 偶然看見這樣一個案例,先上代碼: public class GenericAdd{ //泛型方法實現兩個數相加 public <T extends Number> double add(T t1, T t2){ double sum = 0.0; sum = t1.doubleValue() + t ...
  • MarkDown筆記 目的 寫這篇文章,一來是記錄一下,以備日後使用;二來是我看到網上很多關於MarkDown的語法總結得不是很全面。 語法 1.標題 標題有兩種表示方式,第一種是Atx,這是我見的最多的;第二種是Setext。下麵分別來介紹一下。 (1)Atx 使用 表示,和HTML的h1~h6標 ...
  • 讓我們先來預覽一下代碼運行效果吧: 首先分析163郵箱登陸頁面的網頁結構(按F12或單擊滑鼠右鍵選擇審查元素) 1、定位到登陸框(註意登錄框是一個iframe,如果不定位到iframe的話是無法找到之後的郵箱地址框和密碼輸入框的) 2、定位到郵箱地址框(name='email') 3、定位到密碼輸入 ...
  • C 語言的 static 關鍵字有三種(具體來說是兩種)用途: 1. 靜態局部變數:用於函數體內部修飾變數,這種變數的生存期長於該函數。 要明白這個用法,我們首先要瞭解c/c++的記憶體分佈,以及static所在的區間。 對於一個完整的程式,在記憶體中的分佈情況如下圖: 1.棧區: 由編譯器自動分配釋放 ...
  • 1排版 1 1相對獨立的程式塊之間、變數說明之後必須加空行。 示例:如下例子不符合規範。 if (!valid_ni(ni)) { ... // program code } repssn_ind = ssn_data[index].repssn_index; repssn_ni = ssn_dat ...
  • 本文主要介紹Spring中, 1 Bean 的 init-method 和 destroy-method 2 集合類型的裝配 3 註解方式裝配 4 以自動掃描把組件納入spring容器中管理 5 代理模式 一、Bean 的 init-method 和 destroy-method 以前在學Servl ...
  • 1.JDK API中RandomAccessFile類的描述 此類的實例支持對隨機訪問文件的讀取和寫入。隨機訪問文件的行為類似存儲在文件系統中的一個大型 byte 數組。存在指向該隱含數組的游標或索引,稱為文件指針;輸入操作從文件指針開始讀取位元組,並隨著對位元組的讀取而前移此文件指針。如果隨機訪問文件 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...