java IO 流操作

因爲最近項目有關於大本文讀取和指定行讀取,讀取第多少行到多少行記錄,所以就從網上查詢一些資料和自己的一些代碼總結。
這裏通過一個圖簡單介紹下java的IO流。
IO流分類

  1. 根據留得數據對象區分:
    高端流:所有內存中的流都屬於高端流,比如:InputStreamReader。
    低端流:所有的外界設備中的流都屬於低端流,比如InputStream。
  2. 根據數據的流向區分:
    輸出流:用來寫數據的,由程序(內存)–>>外接設備。
    輸入流:用來讀取數據的,由外界設備–>>程序(內存)。
    區分:輸入流帶有Input,輸出流帶有Output。

  3. 根據流數據的格式區分:
    字節流:處理聲音或者圖片等二進制的數據的流,比如InputStream 。
    字符流:處理文本數據(如txt文件)的流,比如InputStreamReader。

  4. 根據流數據的包裝過程來區分:
    原始流:在實例化流的對象的過程中,不需要另外傳入一個流作爲自己構造方法的參數的流。
    包裝流:在實例化流的對象的過程中,需要傳入另外一個流作爲自己構造方法參數的流。
    區分:所有的低端流都是原始流,所有的高端流都是包裝流。

    IO流對象的繼承關係圖
    這裏寫圖片描述
    關於更爲細緻的分類這裏略過,下面簡單介紹下幾個案例。

    讀取第100萬之後的100條數據。
    方法代碼:

    // 按照行號讀取文本數據
    public static String readByNum(int lineNumber) throws Exception {

        String str = null;
        FileReader fr = new FileReader(path);
        LineNumberReader lr = new LineNumberReader(fr);
        if (lineNumber < 0 || lineNumber > getTotalLines(new File(path))) {
            log.debug("不在文件的行數範圍之內。");
        }
        int i = 1;
        long start = System.currentTimeMillis();
        while((str=lr.readLine())!=null){
            i++;
            if(i>=1000000&&i<1000015){
                log.debug(lr.readLine());
            }
        }
        long end = System.currentTimeMillis();
        fr.close();
        lr.close();

        log.debug("====方法readByNum(int lineNumber)耗時:"+(end-start)+"毫秒");
        return str;
    }

    // 文件內容的總行數。
    public static int getTotalLines(File file) throws IOException {
        FileReader in = new FileReader(file);
        LineNumberReader reader = new LineNumberReader(in);
        String s = reader.readLine();
        int lines = 0;
        while (s != null) {
            lines++;
            s = reader.readLine();
        }
        reader.close();
        in.close();
        return lines;
    }

測試結果如下:因爲數據太大,這裏測試100w的後10條數據

2016-04-12 21:22:17.321 DEBUG [main][ReadbigTxt.java:53] - 0039892655~許水花
2016-04-12 21:22:17.323 DEBUG [main][ReadbigTxt.java:53] - 0036102712~應金蘭
2016-04-12 21:22:17.323 DEBUG [main][ReadbigTxt.java:53] - 0031388582~景德鎮市五金交電化工總公司供應站
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0023055030~胡秀梅
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0043752004~李呂華
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0023914932~汪炳榮
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0024564962~胡香鳳
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0048613725~史三女
2016-04-12 21:22:17.324 DEBUG [main][ReadbigTxt.java:53] - 0043763065~朱永有
2016-04-12 21:22:17.325 DEBUG [main][ReadbigTxt.java:53] - 0053238145~夏松林
2016-04-12 21:22:17.325 DEBUG [main][ReadbigTxt.java:53] - 0040854072~程林嬌
2016-04-12 21:22:17.325 DEBUG [main][ReadbigTxt.java:53] - 0029429342~張榮玖
2016-04-12 21:22:17.325 DEBUG [main][ReadbigTxt.java:53] - 0048657929~董昌國
2016-04-12 21:22:17.325 DEBUG [main][ReadbigTxt.java:53] - 0021460884~周忠德
2016-04-12 21:22:17.326 DEBUG [main][ReadbigTxt.java:53] - 0036288939~陳偉濤
2016-04-12 21:22:17.570 DEBUG [main][ReadbigTxt.java:60] - ====方法readByNum(int lineNumber)耗時:256毫秒

如果文本超過200M以上,需要使用緩存,每次讀取10M放入緩存,操作之後再次讀取。代碼如下:

 /**
     * 讀取txt文件的內容
     * @param file 想要讀取的文件對象
     * 一次讀取一行記錄
     * 每次讀取5m數據放入緩存,讀取5000行存入文本,然後繼續讀取
     * @return 返回文件內容
     * @throws Exception 
     */
    public static String txt2String(File file) throws Exception{
        FileWriter fw = null;
        String path = Utils.getProperty("connectPath2");
        File f = new File(path);
        if (!f.exists()) {
                f.createNewFile();
        }
            //寫到文本
        FileOutputStream fos = new FileOutputStream(f); 
        OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8"); 

        StringBuffer sb = new StringBuffer(); 

        try{
            FileInputStream in = new FileInputStream(file);  
            // 指定讀取文件時以UTF-8的格式讀取  如果文件超過2G可以改爲10M緩存
            BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"),5*1024*1024); //  用5M的緩衝讀取文本文件  
           //BufferedReader br = new BufferedReader(new FileReader(file));//構造一個BufferedReader類來讀取文件
            String s = null;
            int count = 0;//計數器
            while((s = br.readLine())!=null){//使用readLine方法,一次讀一行
                sb.append(s);
                //sb.append(processConnect(s));//這個方法是我對讀取數據的操作,這裏略過直接sb.append(s);
                if (count % 5000 == 0) {// 5000條數據寫入一次
                    out.write(sb.toString());
                    sb.setLength(0);//清空
                    log.debug("====寫入文本成功~");
                }
                count++;
            }



            out.close();
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return sb.toString();
    }

執行之後一個221M的文本讀完並寫入到了另一個文本之中。控制檯輸入如下:

2016-04-12 22:13:56.443 DEBUG [main][ReadTxt2SQL.java:32] - path=F:/OntologyData/tb_data/data.txt
2016-04-12 22:13:58.846 DEBUG [main][ReadTxt2SQL.java:107] - ====寫入文本成功~
2016-04-12 22:13:58.846 DEBUG [main][ReadTxt2SQL.java:109] - ====方法txt2String(File file)耗時:2397毫秒
2016-04-12 22:13:58.847 DEBUG [main][ReadTxt2SQL.java:39] - ====整個文本的處理時間:[2455]毫秒

可能有人會對上面代碼String path = Utils.getProperty("connectPath2");有疑惑,這其實就是讀取文本的路徑,我爲了方便寫了一個通用類,讀取配置文件的內容,可以直接改爲絕對路徑。

可能有人會疑問,爲什麼我要讀取5000條然後存入文本,繼續讀取然後在存,這樣的因爲數據量太大,我們內存空間不足會導致內存溢出,所以需要這樣做。改爲如下:

  while((s = br.readLine())!=null){//使用readLine方法,一次讀一行
                sb.append(s);
                //sb.append(processConnect(s));
                /*if (count % 5000 == 0) {// 5000條數據寫入一次
                    out.write(sb.toString());//寫入文本
                    sb.setLength(0);//清空
                    //log.debug("====寫入文本成功~");
                }*/

                count++;
            }
            out.write(sb.toString());//寫入文本

控制檯打印:

2016-04-12 22:18:58.343 DEBUG [main][ReadTxt2SQL.java:32] - path=F:/OntologyData/tb_data/data.txt
2016-04-12 22:19:03.531 DEBUG [main][ReadTxt2SQL.java:109] - ====寫入文本成功~
2016-04-12 22:19:03.531 DEBUG [main][ReadTxt2SQL.java:111] - ====方法txt2String(File file)耗時:4687毫秒
2016-04-12 22:19:03.661 DEBUG [main][ReadTxt2SQL.java:39] - ====整個文本的處理時間:[5411]毫秒

相對於第一種方法,這種效率低一些,現在我把讀取內容每行都做處理//sb.append(test(s));

2016-04-12 22:24:53.187 DEBUG [main][ReadTxt2SQL.java:32] - path=F:/OntologyData/tb_data/data.txt
2016-04-12 22:24:56.134 DEBUG [main][ReadTxt2SQL.java:107] - ====寫入文本成功~
2016-04-12 22:24:56.135 DEBUG [main][ReadTxt2SQL.java:109] - ====方法txt2String(File file)耗時:2927毫秒
2016-04-12 22:24:56.150 DEBUG [main][ReadTxt2SQL.java:39] - ====整個文本的處理時間:[3014]毫秒

改爲一次讀完再存

2016-04-12 22:25:54.316 DEBUG [main][ReadTxt2SQL.java:32] - path=F:/OntologyData/tb_data/data.txt
2016-04-12 22:25:57.671 DEBUG [main][ReadTxt2SQL.java:108] - ====寫入文本成功~
2016-04-12 22:25:57.672 DEBUG [main][ReadTxt2SQL.java:110] - ====方法txt2String(File file)耗時:3327毫秒
2016-04-12 22:25:57.764 DEBUG [main][ReadTxt2SQL.java:39] - ====整個文本的處理時間:[3499]毫秒

這樣看基本時間差距不大,今天先寫到這裏,等有時間我來測試2G數據,這兩種方式的效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章