現(xiàn)有如下的一個需求,向已存在1G數(shù)據(jù)的txt文本里末尾追加一行文字,內(nèi)容如下“Lucene是一款非常優(yōu)秀的全文檢索庫”??赡艽蠖鄶?shù)朋友會覺得這個需求很easy,說實話,確實easy,然后XXX君開始實現(xiàn)了,直接使用Java中的流讀取了txt文本里原來所有的數(shù)據(jù)轉(zhuǎn)成字符串后,然后拼接了“Lucene是一款非常優(yōu)秀的全文檢索庫”,又寫回文本里了,至此,大功告成。后來需求改了,向5G數(shù)據(jù)的txt文本里追加了,結(jié)果XXX君傻了,他內(nèi)存只有4G,如果強(qiáng)制讀取所有的數(shù)據(jù)并追加,會報內(nèi)存溢出的異常。
其實上面的需求很簡單,如果我們使用JAVA IO體系中的RandomAccessFile類來完成的話,可以實現(xiàn)零內(nèi)存追加。其實這就是支持任意位置讀寫類的強(qiáng)大之處。
在這之前,散仙還是喜歡先啰嗦的介紹下RandomAccessFile這個類,RandomAccessFile是Java中輸入,輸出流體系中功能最豐富的文件內(nèi)容訪問類,它提供很多方法來操作文件,包括讀寫支持,與普通的IO流相比,它最大的特別之處就是支持任意訪問的方式,程序可以直接跳到任意地方來讀寫數(shù)據(jù)。
如果我們只希望訪問文件的部分內(nèi)容,而不是把文件從頭讀到尾,使用RandomAccessFile將會帶來更簡潔的代碼以及更好的性能。
下面來看下RandomAccessFile類中比較重要的2個方法,其他的和普通IO類似,在這里,就不詳細(xì)說明了。
| 方法名 |
作用 |
| getFilePointer() |
返回文件記錄指針的當(dāng)前位置 |
| seek(long pos) |
將文件記錄指針定位到pos的位置 |
下面散仙給出示例,分析下怎么使用RandomAccessFile
首先,我們先看下散仙要操作的文本文件的內(nèi)容截圖。

功能one,讀取任意位置的數(shù)據(jù),代碼如下
Java代碼 
- /**
- * 讀的方法
- * @param path 文件路徑
- * @param pointe 指針位置
- * **/
- public static void randomRed(String path,int pointe){
- try{
- //RandomAccessFile raf=new RandomAccessFile(new File("D:\\3\\test.txt"), "r");
- /**
- * model各個參數(shù)詳解
- * r 代表以只讀方式打開指定文件
- * rw 以讀寫方式打開指定文件
- * rws 讀寫方式打開,并對內(nèi)容或元數(shù)據(jù)都同步寫入底層存儲設(shè)備
- * rwd 讀寫方式打開,對文件內(nèi)容的更新同步更新至底層存儲設(shè)備
- *
- * **/
- RandomAccessFile raf=new RandomAccessFile(path, "r");
- //獲取RandomAccessFile對象文件指針的位置,初始位置是0
- System.out.println("RandomAccessFile文件指針的初始位置:"+raf.getFilePointer());
- raf.seek(pointe);//移動文件指針位置
- byte[] buff=new byte[1024];
- //用于保存實際讀取的字節(jié)數(shù)
- int hasRead=0;
- //循環(huán)讀取
- while((hasRead=raf.read(buff))>0){
- //打印讀取的內(nèi)容,并將字節(jié)轉(zhuǎn)為字符串輸入
- System.out.println(new String(buff,0,hasRead));
-
- }
-
-
-
- }catch(Exception e){
- e.printStackTrace();
- }
-
-
-
- }
測試代碼
Java代碼 
- public static void main(String[] args) {
- String path="D:\\3\\test.txt";
- int seekPointer=20;
- randomRed(path,seekPointer);//讀取的方法
- //randomWrite(path);//追加寫的方法
- //insert(path, 33, "\nlucene是一個優(yōu)秀的全文檢索庫");
- }
運行效果:
Java代碼 
- RandomAccessFile文件指針的初始位置:0
- is a teacher
- hadoop is perfect
功能two,追加數(shù)據(jù),代碼如下
Java代碼 
- /**
- * 追加方式
- * 寫的方法
- * @param path 文件路徑
- * ***/
- public static void randomWrite(String path){
- try{
- /**以讀寫的方式建立一個RandomAccessFile對象**/
- RandomAccessFile raf=new RandomAccessFile(path, "rw");
-
- //將記錄指針移動到文件最后
- raf.seek(raf.length());
- raf.write("我是追加的 \r\n".getBytes());
-
- }catch(Exception e){
- e.printStackTrace();
- }
-
- }
測試代碼
Java代碼 
- public static void main(String[] args) {
- String path="D:\\3\\test.txt";
- //int seekPointer=20;
- // randomRed(path,seekPointer);//讀取的方法
- randomWrite(path);//追加寫的方法
- //insert(path, 33, "\nlucene是一個優(yōu)秀的全文檢索庫");
- }
運行效果:

功能three,任意位置插入數(shù)據(jù),代碼如下
Java代碼 
- /**
- * 實現(xiàn)向指定位置
- * 插入數(shù)據(jù)
- * @param fileName 文件名
- * @param points 指針位置
- * @param insertContent 插入內(nèi)容
- * **/
- public static void insert(String fileName,long points,String insertContent){
- try{
- File tmp=File.createTempFile("tmp", null);
- tmp.deleteOnExit();//在JVM退出時刪除
-
- RandomAccessFile raf=new RandomAccessFile(fileName, "rw");
- //創(chuàng)建一個臨時文件夾來保存插入點后的數(shù)據(jù)
- FileOutputStream tmpOut=new FileOutputStream(tmp);
- FileInputStream tmpIn=new FileInputStream(tmp);
- raf.seek(points);
- /**將插入點后的內(nèi)容讀入臨時文件夾**/
-
- byte [] buff=new byte[1024];
- //用于保存臨時讀取的字節(jié)數(shù)
- int hasRead=0;
- //循環(huán)讀取插入點后的內(nèi)容
- while((hasRead=raf.read(buff))>0){
- // 將讀取的數(shù)據(jù)寫入臨時文件中
- tmpOut.write(buff, 0, hasRead);
- }
-
- //插入需要指定添加的數(shù)據(jù)
- raf.seek(points);//返回原來的插入處
- //追加需要追加的內(nèi)容
- raf.write(insertContent.getBytes());
- //最后追加臨時文件中的內(nèi)容
- while((hasRead=tmpIn.read(buff))>0){
- raf.write(buff,0,hasRead);
- }
- }catch(Exception e){
- e.printStackTrace();
- }
- }
測試代碼
Java代碼 
- public static void main(String[] args) {
- String path="D:\\3\\test.txt";
- //int seekPointer=20;
- // randomRed(path,seekPointer);//讀取的方法
- // randomWrite(path);//追加寫的方法
- insert(path, 33, "\nlucene是一個優(yōu)秀的全文檢索庫");
- }
運行效果:

至此,RandomAccessFile類的幾個功能,散仙在代碼中已給出實現(xiàn)了,現(xiàn)在回到本文開始前的提的那個需求,用RandomAccessFile類就可以輕而易舉的完成了,另外需要注意的是,向指定位置插入數(shù)據(jù),是散仙自己改造的功能,RandomAccessFile并不直接支持,需要新建一個緩沖區(qū)臨時空間,存數(shù)據(jù),然后在寫,因為一旦數(shù)據(jù)量上了級別,在任意位置插入數(shù)據(jù),是很耗內(nèi)存的,這個也就是為什么hadoop的HDFS文件系統(tǒng),只支持append的方式,而沒有提供修改的操作。
另外我們可以用RandomAccessFile這個類,來實現(xiàn)一個多線程斷點下載的功能,用過下載工具的朋友們都知道,下載前都會建立兩個臨時文件,一個是與被下載文件大小相同的空文件,另一個是記錄文件指針的位置文件,每次暫停的時候,都會保存上一次的指針,然后斷點下載的時候,會繼續(xù)從上一次的地方下載,從而實現(xiàn)斷點下載或上傳的功能,有興趣的朋友們可以自己實現(xiàn)下。
|