因爲最近項目有關於大本文讀取和指定行讀取,讀取第多少行到多少行記錄,所以就從網上查詢一些資料和自己的一些代碼總結。
這裏通過一個圖簡單介紹下java的IO流。
IO流分類
- 根據留得數據對象區分:
高端流:所有內存中的流都屬於高端流,比如:InputStreamReader。
低端流:所有的外界設備中的流都屬於低端流,比如InputStream。 根據數據的流向區分:
輸出流:用來寫數據的,由程序(內存)–>>外接設備。
輸入流:用來讀取數據的,由外界設備–>>程序(內存)。
區分:輸入流帶有Input,輸出流帶有Output。根據流數據的格式區分:
字節流:處理聲音或者圖片等二進制的數據的流,比如InputStream 。
字符流:處理文本數據(如txt文件)的流,比如InputStreamReader。根據流數據的包裝過程來區分:
原始流:在實例化流的對象的過程中,不需要另外傳入一個流作爲自己構造方法的參數的流。
包裝流:在實例化流的對象的過程中,需要傳入另外一個流作爲自己構造方法參數的流。
區分:所有的低端流都是原始流,所有的高端流都是包裝流。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數據,這兩種方式的效率。