Java RandomAccessFile用法

来源:http://www.cnblogs.com/sikewang/archive/2016/06/06/5564765.html
-Advertisement-
Play Games

RandomAccessFile RandomAccessFile是用來訪問那些保存數據記錄的文件的,你就可以用seek( )方法來訪問記錄,併進行讀寫了。這些記錄的大小不必相同;但是其大小和位置必須是可知的。但是該類僅限於操作文件。 RandomAccessFile不屬於InputStream和O ...


RandomAccessFile

RandomAccessFile是用來訪問那些保存數據記錄的文件的,你就可以用seek( )方法來訪問記錄,併進行讀寫了。這些記錄的大小不必相同;但是其大小和位置必須是可知的。但是該類僅限於操作文件。

RandomAccessFile不屬於InputStream和OutputStream類系的。實際上,除了實現DataInput和 DataOutput介面之外(DataInputStream和DataOutputStream也實現了這兩個介面),它和這兩個類系毫不相干,甚至 不使用InputStream和OutputStream類中已經存在的任何功能;它是一個完全獨立的類,所有方法(絕大多數都只屬於它自己)都是從零開 始寫的。這可能是因為RandomAccessFile能在文件裡面前後移動,所以它的行為與其它的I/O類有些根本性的不同。總而言之,它是一個直接繼 承Object的,獨立的類。

基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream結合起來,再加上它自己的 一些方法,比如定位用的getFilePointer( ),在文件里移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少位元組數。此外,它的構造函數還要一個表示以只讀方式("r"),還是以讀寫方式("rw")打開文件的參數 (和C的fopen( )一模一樣)。它不支持只寫文件。

只有RandomAccessFile才有seek搜尋方法,而這個方法也只適用於文件。BufferedInputStream有一個mark( )方法,你可以用它來設定標記(把結果保存在一個內部變數里),然後再調用reset( )返回這個位置,但是它的功能太弱了,而且也不怎麼實用。

RandomAccessFile的絕大多數功能,但不是全部,已經被JDK 1.4的nio的"記憶體映射文件(memory-mapped files)"給取代了,你該考慮一下是不是用"記憶體映射文件"來代替RandomAccessFile了。

[java] view plain copy
  1. import java.io.IOException;  
  2. import java.io.RandomAccessFile;  
  3.   
  4. public class TestRandomAccessFile {  
  5.     public static void main(String[] args) throws IOException {  
  6.         RandomAccessFile rf = new RandomAccessFile("rtest.dat", "rw");  
  7.         for (int i = 0; i < 10; i++) {  
  8.             //寫入基本類型double數據  
  9.             rf.writeDouble(i * 1.414);  
  10.         }  
  11.         rf.close();  
  12.         rf = new RandomAccessFile("rtest.dat", "rw");  
  13.         //直接將文件指針移到第5個double數據後面  
  14.         rf.seek(5 * 8);  
  15.         //覆蓋第6個double數據  
  16.         rf.writeDouble(47.0001);  
  17.         rf.close();  
  18.         rf = new RandomAccessFile("rtest.dat", "r");  
  19.         for (int i = 0; i < 10; i++) {  
  20.             System.out.println("Value " + i + ": " + rf.readDouble());  
  21.         }  
  22.         rf.close();  
  23.     }  
  24. }   


 

記憶體映射文件

記憶體映射文件能讓你創建和修改那些因為太大而無法放入記憶體的文件。有了記憶體映射文件,你就可以認為文件已經全部讀進了記憶體,然後把它當成一個非常大的數組來訪問。這種解決辦法能大大簡化修改文件的代碼。
fileChannel.map(FileChannel.MapMode mode, long position, long size)將此通道的文件區域直接映射到記憶體中。註意,你必須指明,它是從文件的哪個位置開始映射的,映射的範圍又有多大;也就是說,它還可以映射一個大 文件的某個小片斷。


MappedByteBuffer是ByteBuffer的子類,因此它具備了 ByteBuffer的所有方法,但新添了force()將緩衝區的內容強制刷新到存儲設備中去、load()將存儲設備中的數據載入到記憶體中、 isLoaded()位置記憶體中的數據是否與存儲設置上同步。這裡只簡單地演示了一下put()和get()方法,除此之外,你還可以使用 asCharBuffer( )之類的方法得到相應基本類型數據的緩衝視圖後,可以方便的讀寫基本類型數據。

[java] view plain copy
  1. import java.io.RandomAccessFile;  
  2. import java.nio.MappedByteBuffer;  
  3. import java.nio.channels.FileChannel;  
  4.   
  5. public class LargeMappedFiles {  
  6.     static int length = 0x8000000; // 128 Mb  
  7.   
  8.     public static void main(String[] args) throws Exception {  
  9.         // 為了以可讀可寫的方式打開文件,這裡使用RandomAccessFile來創建文件。  
  10.         FileChannel fc = new RandomAccessFile("test.dat", "rw").getChannel();  
  11.         //註意,文件通道的可讀可寫要建立在文件流本身可讀寫的基礎之上  
  12.         MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);  
  13.         //寫128M的內容  
  14.         for (int i = 0; i < length; i++) {  
  15.             out.put((byte) 'x');  
  16.         }  
  17.         System.out.println("Finished writing");  
  18.         //讀取文件中間6個位元組內容  
  19.         for (int i = length / 2; i < length / 2 + 6; i++) {  
  20.             System.out.print((char) out.get(i));  
  21.         }  
  22.         fc.close();  
  23.     }  
  24. }  


 

儘管映射寫似乎要用到FileOutputStream,但是映射文件中的所有輸出 必須使用RandomAccessFile,但如果只需要讀時可以使用FileInputStream,寫映射文件時一定要使用隨機訪問文件,可能寫時要讀的原因吧。

 

該程式創建了一個128Mb的文件,如果一次性讀到記憶體可能導致記憶體溢出,但這裡訪問好像只是一瞬間的事,這是因為,真正調入記憶體的只是其中的一小部分,其餘部分則被放在交換文件上。這樣你就可以很方便地修改超大型的文件了(最大可以到2 GB)。註意,Java是調用操作系統的"文件映射機制"來提升性能的。

 

RandomAccessFile類的應用:

[java] view plain copy
  1. /* 
  2.  * 程式功能:演示了RandomAccessFile類的操作,同時實現了一個文件複製操作。 
  3.  */  
  4. package com.lwj.demo;  
  5.   
  6. import java.io.*;  
  7.   
  8. public class RandomAccessFileDemo {  
  9.  public static void main(String[] args) throws Exception {  
  10.   RandomAccessFile file = new RandomAccessFile("file", "rw");  
  11.   // 以下向file文件中寫數據  
  12.   file.writeInt(20);// 占4個位元組  
  13.   file.writeDouble(8.236598);// 占8個位元組  
  14.   file.writeUTF("這是一個UTF字元串");// 這個長度寫在當前文件指針的前兩個位元組處,可用readShort()讀取  
  15.   file.writeBoolean(true);// 占1個位元組  
  16.   file.writeShort(395);// 占2個位元組  
  17.   file.writeLong(2325451l);// 占8個位元組  
  18.   file.writeUTF("又是一個UTF字元串");  
  19.   file.writeFloat(35.5f);// 占4個位元組  
  20.   file.writeChar('a');// 占2個位元組  
  21.   
  22.   file.seek(0);// 把文件指針位置設置到文件起始處  
  23.   
  24.   // 以下從file文件中讀數據,要註意文件指針的位置  
  25.   System.out.println("——————從file文件指定位置讀數據——————");  
  26.   System.out.println(file.readInt());  
  27.   System.out.println(file.readDouble());  
  28.   System.out.println(file.readUTF());  
  29.   
  30.   file.skipBytes(3);// 將文件指針跳過3個位元組,本例中即跳過了一個boolean值和short值。  
  31.   System.out.println(file.readLong());  
  32.   
  33.   file.skipBytes(file.readShort()); // 跳過文件中“又是一個UTF字元串”所占位元組,註意readShort()方法會移動文件指針,所以不用加2。  
  34.   System.out.println(file.readFloat());  
  35.     
  36.   //以下演示文件複製操作  
  37.   System.out.println("——————文件複製(從file到fileCopy)——————");  
  38.   file.seek(0);  
  39.   RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");  
  40.   int len=(int)file.length();//取得文件長度(位元組數)  
  41.   byte[] b=new byte[len];  
  42.   file.readFully(b);  
  43.   fileCopy.write(b);  
  44.   System.out.println("複製完成!");  
  45.  }  
  46. }  


RandomAccessFile 插入寫示例:

[java] view plain copy
  1. /** 
  2.  *  
  3.  * @param skip 跳過多少過位元組進行插入數據 
  4.  * @param str 要插入的字元串 
  5.  * @param fileName 文件路徑 
  6.  */  
  7. public static void beiju(long skip, String str, String fileName){  
  8.     try {  
  9.         RandomAccessFile raf = new RandomAccessFile(fileName,"rw");  
  10.         if(skip <  0 || skip > raf.length()){  
  11.             System.out.println("跳過位元組數無效");  
  12.             return;  
  13.         }  
  14.         byte[] b = str.getBytes();  
  15.         raf.setLength(raf.length() + b.length);  
  16.         for(long i = raf.length() - 1; i > b.length + skip - 1; i--){  
  17.             raf.seek(i - b.length);  
  18.             byte temp = raf.readByte();  
  19.             raf.seek(i);  
  20.             raf.writeByte(temp);  
  21.         }  
  22.         raf.seek(skip);  
  23.         raf.write(b);  
  24.         raf.close();  
  25.     } catch (Exception e) {  
  26.         e.printStackTrace();  
  27.     }  
  28. }  


 

利用RandomAccessFile實現文件的多線程下載,即多線程下載一個文件時,將文件分成幾塊,每塊用不同的線程進行下載。下麵是一個利用多線程在寫文件時的例子,其中預先分配文件所需要的空間,然後在所分配的空間中進行分塊,然後寫入:

[java] view plain copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4.   
  5. /** 
  6.  * 測試利用多線程進行文件的寫操作 
  7.  */  
  8. public class Test {  
  9.   
  10.     public static void main(String[] args) throws Exception {  
  11.         // 預分配文件所占的磁碟空間,磁碟中會創建一個指定大小的文件  
  12.         RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");  
  13.         raf.setLength(1024*1024); // 預分配 1M 的文件空間  
  14.         raf.close();  
  15.           
  16.         // 所要寫入的文件內容  
  17.         String s1 = "第一個字元串";  
  18.         String s2 = "第二個字元串";  
  19.         String s3 = "第三個字元串";  
  20.         String s4 = "第四個字元串";  
  21.         String s5 = "第五個字元串";  
  22.           
  23.         // 利用多線程同時寫入一個文件  
  24.         new FileWriteThread(1024*1,s1.getBytes()).start(); // 從文件的1024位元組之後開始寫入數據  
  25.         new FileWriteThread(1024*2,s2.getBytes()).start(); // 從文件的2048位元組之後開始寫入數據  
  26.         new FileWriteThread(1024*3,s3.getBytes()).start(); // 從文件的3072位元組之後開始寫入數據  
  27.         new FileWriteThread(1024*4,s4.getBytes()).start(); // 從文件的4096位元組之後開始寫入數據  
  28.         new FileWriteThread(1024*5,s5.getBytes()).start(); // 從文件的5120位元組之後開始寫入數據  
  29.     }  
  30.       
  31.     // 利用線程在文件的指定位置寫入指定數據  
  32.     static class FileWriteThread extends Thread{  
  33.         private int skip;  
  34.         private byte[] content;  
  35.           
  36.         public FileWriteThread(int skip,byte[] content){  
  37.             this.skip = skip;  
  38.             this.content = content;  
  39.         }  
  40.           
  41.         public void run(){  
  42.             RandomAccessFile raf = null;  
  43.             try {  
  44.                 raf = new RandomAccessFile("D://abc.txt", "rw");  
  45.                 raf.seek(skip);  
  46.                 raf.write(content);  
  47.             } catch (FileNotFoundException e) {  
  48.                 e.printStackTrace();  
  49.             } catch (IOException e) {  
  50.                 // TODO Auto-generated catch block  
  51.                 e.printStackTrace();  
  52.             } finally {  
  53.                 try {  
  54.                     raf.close();  
  55.                 } catch (Exception e) {  
  56.                 }  
  57.             }  
  58.         }  
  59.     }  
  60.   

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

-Advertisement-
Play Games
更多相關文章
  • 兩個線程操作同一個資源,比如,輸入和輸出,操作同一個對象,此時兩個線程會爭奪cpu的執行權,隨機的進行切換。我們想實現先輸入再輸出,順序的執行 目標對象定義一個標記欄位,進行判斷,wait()和notify()方法 wait()方法,線程會處於等待狀態,等待的線程位於記憶體中的線程池中 notify( ...
  • 對自己定義的類規範化一下,事件和圖形化組件分離出來 定義一個類FrameDemo 定義成員屬性Frame frame 定義成員屬性Botton 定義構造方法FrameDemo() 定義初始化方法init() 初始化方法中,new出來Frame(),參數:String的窗體名稱 調用Frame對象的s ...
  • switch允許對一個標量(表達式)的多個可能結果做選擇。 語法: 系統計算expr的值,根據計算結果(result1、result2等)來選擇下麵對應執行語句,如果所有的case結果都不符合,則會執行default里的語句。 例子: 運行 提示 可以有多個 case 條件判斷 case 後面結果也 ...
  • 我們在中級篇中學會瞭如何進行反向解析,但是有這樣一個問題,在為 url 命名的時候,名字不能重覆,否則會導致各種各樣的問題。在 url 還少的時候保證不重名還是比較簡單的,但是 url 多起來以後就比較難了。為瞭解決這樣的問題,可以在 url 中加一個首碼。例如,我有一個 url 的名字叫做 'co ...
  • 輸入輸出流 1. 用控制符輸出格式,例: 控制符用法列表如下: 2. 用流對象cout中用於控制輸出格式的成員函數來控制輸出格式 例如: cout.setf(iso::showbase) cout.setf(iOS::oct) cout.width(10) cout.setf(ios::intern ...
  • 繼續瞭解controller基類。 ...
  • Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted i ...
  • 面向對象的概念經常會有一些特別的搭配,如:面向對象的分析、面向對象的設計、以及我們下麵講到的面向對象的編程實現。 在我們編程的時候,經常會需要描述一些有特性的東西,這些特性是外在的或者可以表現的。 而我們在編程的時候,需要根據這種東西的特性對其操作,也就是“抽象成一個類”的過程。 C++對於類內的成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...