java實現(xiàn)大文本文件拆分
本文實例為大家分享了java實現(xiàn)大文本文件拆分的具體代碼,供大家參考,具體內(nèi)容如下
生成大文件
public static void createBigFile() throws IOException { File file = new File("/Users/yangpeng/Documents/temp/big_file.csv"); FileWriter fileWriter = new FileWriter(file); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1"; for (int i = 0; i < 1000000; i++) { bufferedWriter.write(str); bufferedWriter.newLine(); } bufferedWriter.flush(); bufferedWriter.close(); }
文件拆分
此處沒有給出根據(jù)文件大小計算需要拆分的文件數(shù)量,所以這里是給定一個拆分文件數(shù)量
思路
思路:給定帶拆分數(shù)量,計算出每個文件的平均字節(jié)數(shù),然后循環(huán)文件數(shù)進行每個文件的拆分。拆分第一個文件時,根據(jù)平均字節(jié)數(shù)往后取給定的大約行字節(jié)數(shù)的字節(jié),然后循環(huán)字節(jié)判斷是否為\r或者\n,如果字節(jié)為\r或者\n則代表到達行末尾,記錄行尾字節(jié)位置。知道了開頭字節(jié)位置與結(jié)束字節(jié)位置,就可以將此位置之間的數(shù)據(jù)生成子文件了。繼續(xù)循環(huán)拆分下個文件,基于上個文件記錄的結(jié)束字節(jié)位置繼續(xù)計算當(dāng)前文件的結(jié)束位置,直到到達拆分文件的數(shù)量或者大文件讀取完畢。
舉個栗子:
有一個3行記錄的文件,假設(shè)每行記錄行字節(jié)包含換行符的字節(jié)數(shù)為100,也就是說這個文件的總字節(jié)數(shù)為300。
我現(xiàn)在要將這個文件拆分成2個。按照上面的思路,首先我需要計算出文件的平均值300/2=150,這里計算出的平均值并不是拆分出來的子文件一定是150,因為這個數(shù)字位置的字節(jié)有可能在一行的中間,那么我要基于這個數(shù)字算出下個換行符出現(xiàn)的位置當(dāng)做我這個子文件的結(jié)束位。
所以我給定一個行字節(jié)數(shù)100+150=250,這個150到250之間的字節(jié)我認為有換行符,所以我輪詢這100字節(jié),判斷是否為換行符,結(jié)果我輪到到50的位置發(fā)現(xiàn)了換行。
那么我這個第一個文件的結(jié)束位置是150+50=200,然后將0到200之間的字節(jié)生成第一個文件。然后基于這個200的位置繼續(xù)拆分下個文件,由于200+150已經(jīng)大于了源文件的大小,所以直接將200到300的數(shù)據(jù)生成一個子文件。所以最終的結(jié)果是一二行為一個子文件,三行為第二個子文件。
代碼
考慮到性能與內(nèi)存占用的問題,此處實現(xiàn)采用NIO
public static void splitFile(String filePath, int fileCount) throws IOException { FileInputStream fis = new FileInputStream(filePath); FileChannel inputChannel = fis.getChannel(); final long fileSize = inputChannel.size(); long average = fileSize / fileCount;//平均值 long bufferSize = 200; //緩存塊大小,自行調(diào)整 ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.valueOf(bufferSize + "")); // 申請一個緩存區(qū) long startPosition = 0; //子文件開始位置 long endPosition = average < bufferSize ? 0 : average - bufferSize;//子文件結(jié)束位置 for (int i = 0; i < fileCount; i++) { if (i + 1 != fileCount) { int read = inputChannel.read(byteBuffer, endPosition);// 讀取數(shù)據(jù) readW: while (read != -1) { byteBuffer.flip();//切換讀模式 byte[] array = byteBuffer.array(); for (int j = 0; j < array.length; j++) { byte b = array[j]; if (b == 10 || b == 13) { //判斷\n\r endPosition += j; break readW; } } endPosition += bufferSize; byteBuffer.clear(); //重置緩存塊指針 read = inputChannel.read(byteBuffer, endPosition); } }else{ endPosition = fileSize; //最后一個文件直接指向文件末尾 } FileOutputStream fos = new FileOutputStream(filePath + (i + 1)); FileChannel outputChannel = fos.getChannel(); inputChannel.transferTo(startPosition, endPosition - startPosition, outputChannel);//通道傳輸文件數(shù)據(jù) outputChannel.close(); fos.close(); startPosition = endPosition + 1; endPosition += average; } inputChannel.close(); fis.close(); } public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); scanner.nextLine(); long startTime = System.currentTimeMillis(); splitFile("/Users/yangpeng/Documents/temp/big_file.csv",5); long endTime = System.currentTimeMillis(); System.out.println("耗費時間: " + (endTime - startTime) + " ms"); scanner.nextLine(); }
使用NIO可以高效的實現(xiàn)文件拆分,我的文件為100W行大小為1.02G的文本文件,拆分成5個子文件總耗時1224ms
后如下是使用jvisualvm監(jiān)控的程序內(nèi)存:
可以看到拆分期間內(nèi)存浮動基本在1M左右。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flyway詳解及Springboot集成Flyway的詳細教程
Flayway是一款數(shù)據(jù)庫版本控制管理工具,,支持數(shù)據(jù)庫版本自動升級,Migrations可以寫成sql腳本,也可以寫在java代碼里。這篇文章主要介紹了Flyway詳解及Springboot集成Flyway的詳細教程的相關(guān)資料,需要的朋友可以參考下2020-07-07解決SpringBoot jar包中的文件讀取問題實現(xiàn)
這篇文章主要介紹了解決SpringBoot jar包中的文件讀取問題實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08關(guān)于protected修飾符詳解-源于Cloneable接口
這篇文章主要介紹了protected修飾符詳解-源于Cloneable接口,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11