Java使用RandomAccessFile读写文件

<div class="markdown_views"> <p>[TOC] <br> 转载自:<a href="http://blog.csdn.net/akon_vm/article/details/7429245" target="_blank">http://blog.csdn.net/akon_vm/article/details/7429245</a></p>java

<h2 id="java-randomaccessfile"><a name="t0"></a>Java RandomAccessFile</h2>数组

<p>RandomAccessFile是用来访问那些保存数据记录的文件的,你就能够用seek( )方法来访问记录,并进行读写了。这些记录的大小没必要相同;可是其大小和位置必须是可知的。可是该类仅限于操做文件。</p>markdown

<p>RandomAccessFile不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口以外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系绝不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个彻底独立的类,全部方法(绝大多数都只属于它本身)都是从零开始写的。这多是由于RandomAccessFile能在文件里面先后移动,因此它的行为与其它的I/O类有些根本性的不一样。总而言之,它是一个直接继承Object的,独立的类。</p>多线程

<p>基本上,RandomAccessFile的工做方式是,把DataInputStream和DataOutputStream结合起来,再加上它本身的一些方法,好比定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。此外,它的构造函数还要一个表示以只读方式(“r”),仍是以读写方式(“rw”)打开文件的参数 (和C的fopen( )如出一辙)。它不支持只写文件。</p>app

<p>只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你能够用它来设定标记(把结果保存在一个内部变量里),而后再调用reset( )返回这个位置,可是它的功能太弱了,并且也不怎么实用。</p>dom

<p>RandomAccessFile的绝大多数功能,但不是所有,已经被JDK 1.4的nio的”内存映射文件(memory-mapped files)”给取代了,你该考虑一下是否是用”内存映射文件”来代替RandomAccessFile了。</p>函数

<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.IOException; </br> <span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestRandomAccessFile</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> IOException { </br> RandomAccessFile rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) { </br> <span class="hljs-comment">//写入基本类型double数据 </span></br> rf.writeDouble(i * <span class="hljs-number">1.414</span>); </br> } </br> rf.close(); </br> rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-comment">//直接将文件指针移到第5个double数据后面 </span></br> rf.seek(<span class="hljs-number">5</span> * <span class="hljs-number">8</span>); </br> <span class="hljs-comment">//覆盖第6个double数据 </span></br> rf.writeDouble(<span class="hljs-number">47.0001</span>); </br> rf.close(); </br> rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"r"</span>); </br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) { </br> System.out.println(<span class="hljs-string">"Value "</span> + i + <span class="hljs-string">": "</span> + rf.readDouble()); </br> } </br> rf.close(); </br> } </br> } </code></pre>性能

<h2 id="内存映射文件"><a name="t1"></a>内存映射文件</h2>测试

<p>内存映射文件能让你建立和修改那些由于太大而没法放入内存的文件。有了内存映射文件,你就能够认为文件已经所有读进了内存,而后把它当成一个很是大的数组来访问。这种解决办法能大大简化修改文件的代码。 <br> fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪一个位置开始映射的,映射的范围又有多大;也就是说,它还能够映射一个大文件的某个小片段。</p>this

<p>MappedByteBuffer是ByteBuffer的子类,所以它具有了ByteBuffer的全部方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和get()方法,除此以外,你还可使用asCharBuffer( )之类的方法获得相应基本类型数据的缓冲视图后,能够方便的读写基本类型数据。</p>

<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-keyword">import</span> java.nio.MappedByteBuffer; </br> <span class="hljs-keyword">import</span> java.nio.channels.FileChannel; </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LargeMappedFiles</span> {</span> </br> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> length = <span class="hljs-number">0x8000000</span>; <span class="hljs-comment">// 128 Mb </span></br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> <span class="hljs-comment">// 为了以可读可写的方式打开文件,这里使用RandomAccessFile来建立文件。 </span></br> FileChannel fc = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"test.dat"</span>, <span class="hljs-string">"rw"</span>).getChannel(); </br> <span class="hljs-comment">//注意,文件通道的可读可写要创建在文件流自己可读写的基础之上 </span></br> MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, <span class="hljs-number">0</span>, length); </br> <span class="hljs-comment">//写128M的内容 </span></br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; length; i++) { </br> out.put((<span class="hljs-keyword">byte</span>) <span class="hljs-string">'x'</span>); </br> } </br> System.out.println(<span class="hljs-string">"Finished writing"</span>); </br> <span class="hljs-comment">//读取文件中间6个字节内容 </span></br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = length / <span class="hljs-number">2</span>; i &lt; length / <span class="hljs-number">2</span> + <span class="hljs-number">6</span>; i++) { </br> System.out.print((<span class="hljs-keyword">char</span>) out.get(i)); </br> } </br> fc.close(); </br> } </br> }</code></pre>

<blockquote> <p><strong>尽管映射写彷佛要用到FileOutputStream,可是映射文件中的全部输出 必须使用RandomAccessFile,但若是只须要读时可使用FileInputStream,写映射文件时必定要使用随机访问文件,可能写时要读的缘由吧。</strong></p> </blockquote>

<p>该程序建立了一个128Mb的文件,若是一次性读到内存可能致使内存溢出,但这里访问好像只是一瞬间的事,这是由于,真正调入内存的只是其中的一小部分,其他部分则被放在交换文件上。这样你就能够很方便地修改超大型的文件了(最大能够到2 GB)。注意,Java是调用操做系统的”文件映射机制”来提高性能的。</p>

<h2 id="randomaccessfile类的应用"><a name="t2"></a>RandomAccessFile类的应用:</h2>

<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-comment">/* * 程序功能:演示了RandomAccessFile类的操做,同时实现了一个文件复制操做。 */</span> <span class="hljs-keyword">package</span> com.lwj.demo; <span class="hljs-keyword">import</span> java.io.*; <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RandomAccessFileDemo</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> RandomAccessFile file = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"file"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-comment">// 如下向file文件中写数据 </span></br> file.writeInt(<span class="hljs-number">20</span>);<span class="hljs-comment">// 占4个字节 </span></br> file.writeDouble(<span class="hljs-number">8.236598</span>);<span class="hljs-comment">// 占8个字节 </span></br> file.writeUTF(<span class="hljs-string">"这是一个UTF字符串"</span>);<span class="hljs-comment">// 这个长度写在当前文件指针的前两个字节处,可用readShort()读取 </span></br> file.writeBoolean(<span class="hljs-keyword">true</span>);<span class="hljs-comment">// 占1个字节 </span></br> file.writeShort(<span class="hljs-number">395</span>);<span class="hljs-comment">// 占2个字节 </span></br> file.writeLong(<span class="hljs-number">2325451</span>l);<span class="hljs-comment">// 占8个字节 </span></br> file.writeUTF(<span class="hljs-string">"又是一个UTF字符串"</span>); </br> file.writeFloat(<span class="hljs-number">35.5</span>f);<span class="hljs-comment">// 占4个字节 </span></br> file.writeChar(<span class="hljs-string">'a'</span>);<span class="hljs-comment">// 占2个字节 </span></br> </br> file.seek(<span class="hljs-number">0</span>);<span class="hljs-comment">// 把文件指针位置设置到文件起始处 </span></br> </br> <span class="hljs-comment">// 如下从file文件中读数据,要注意文件指针的位置 </span></br> System.out.println(<span class="hljs-string">"——————从file文件指定位置读数据——————"</span>); </br> System.out.println(file.readInt()); </br> System.out.println(file.readDouble()); </br> System.out.println(file.readUTF()); </br> </br> file.skipBytes(<span class="hljs-number">3</span>);<span class="hljs-comment">// 将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。 </span></br> System.out.println(file.readLong()); </br> </br> file.skipBytes(file.readShort()); <span class="hljs-comment">// 跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,因此不用加2。 </span></br> System.out.println(file.readFloat()); </br> </br> <span class="hljs-comment">//如下演示文件复制操做 </span></br> System.out.println(<span class="hljs-string">"——————文件复制(从file到fileCopy)——————"</span>); </br> file.seek(<span class="hljs-number">0</span>); </br> RandomAccessFile fileCopy=<span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"fileCopy"</span>,<span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">int</span> len=(<span class="hljs-keyword">int</span>)file.length();<span class="hljs-comment">//取得文件长度(字节数) </span></br> <span class="hljs-keyword">byte</span>[] b=<span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[len]; file.readFully(b); </br> fileCopy.write(b); </br> System.out.println(<span class="hljs-string">"复制完成!"</span>); </br> } </br> }</code></pre>

<h2 id="randomaccessfile-插入写示例"><a name="t3"></a>RandomAccessFile 插入写示例:</h2>

<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-javadoc">/** </br> * </br> *<span class="hljs-javadoctag"> @param</span> skip 跳过多少过字节进行插入数据 </br> *<span class="hljs-javadoctag"> @param</span> str 要插入的字符串 </br> *<span class="hljs-javadoctag"> @param</span> fileName 文件路径</br> */</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">beiju</span>(<span class="hljs-keyword">long</span> skip, String str, String fileName){ </br> <span class="hljs-keyword">try</span> { </br> RandomAccessFile raf = <span class="hljs-keyword">new</span> RandomAccessFile(fileName,<span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">if</span>(skip &lt; <span class="hljs-number">0</span> || skip &gt; raf.length()){ </br> System.out.println(<span class="hljs-string">"跳过字节数无效"</span>); </br> <span class="hljs-keyword">return</span>; </br> } </br> <span class="hljs-keyword">byte</span>[] b = str.getBytes(); </br> raf.setLength(raf.length() + b.length); </br> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">long</span> i = raf.length() - <span class="hljs-number">1</span>; i &gt; b.length + skip - <span class="hljs-number">1</span>; i--){ </br> raf.seek(i - b.length); </br> <span class="hljs-keyword">byte</span> temp = raf.readByte(); </br> raf.seek(i); </br> raf.writeByte(temp); </br> } </br> raf.seek(skip); </br> raf.write(b); </br> raf.close(); </br> } <span class="hljs-keyword">catch</span> (Exception e) { </br> e.printStackTrace(); </br> } </br> }</code></pre>

<p>利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分红几块,每块用不一样的线程进行下载。下面是一个利用多线程在写文件时的例子,其中预先分配文件所须要的空间,而后在所分配的空间中进行分块,而后写入:</p>

<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.FileNotFoundException; </br> <span class="hljs-keyword">import</span> java.io.IOException; </br> <span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-javadoc">/** </br> * 测试利用多线程进行文件的写操做 </br> */</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> <span class="hljs-comment">// 预分配文件所占的磁盘空间,磁盘中会建立一个指定大小的文件 </span></br> RandomAccessFile raf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"D://abc.txt"</span>, <span class="hljs-string">"rw"</span>); </br> raf.setLength(<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>); <span class="hljs-comment">// 预分配 1M 的文件空间 </span></br> raf.close(); </br> </br> <span class="hljs-comment">// 所要写入的文件内容 </span></br> String s1 = <span class="hljs-string">"第一个字符串"</span>; </br> String s2 = <span class="hljs-string">"第二个字符串"</span>; </br> String s3 = <span class="hljs-string">"第三个字符串"</span>; </br> String s4 = <span class="hljs-string">"第四个字符串"</span>; </br> String s5 = <span class="hljs-string">"第五个字符串"</span>; </br> </br> <span class="hljs-comment">// 利用多线程同时写入一个文件 </span> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">1</span>,s1.getBytes()).start(); <span class="hljs-comment">// 从文件的1024字节以后开始写入数据 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">2</span>,s2.getBytes()).start(); <span class="hljs-comment">// 从文件的2048字节以后开始写入数据 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">3</span>,s3.getBytes()).start(); <span class="hljs-comment">// 从文件的3072字节以后开始写入数据 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">4</span>,s4.getBytes()).start(); <span class="hljs-comment">// 从文件的4096字节以后开始写入数据 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">5</span>,s5.getBytes()).start(); <span class="hljs-comment">// 从文件的5120字节以后开始写入数据 </span></br> } </br> <span class="hljs-comment">// 利用线程在文件的指定位置写入指定数据 </span></br> <span class="hljs-keyword">static</span> class FileWriteThread extends Thread{ </br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> skip; </br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">byte</span>[] content; </br> <span class="hljs-keyword">public</span> <span class="hljs-title">FileWriteThread</span>(<span class="hljs-keyword">int</span> skip,<span class="hljs-keyword">byte</span>[] content){ <span class="hljs-keyword">this</span>.skip = skip; </br> <span class="hljs-keyword">this</span>.content = content; </br> } </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>(){ </br> RandomAccessFile raf = <span class="hljs-keyword">null</span>; </br> <span class="hljs-keyword">try</span> { </br> raf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"D://abc.txt"</span>, <span class="hljs-string">"rw"</span>); </br> raf.seek(skip); </br> raf.write(content); </br> } <span class="hljs-keyword">catch</span> (FileNotFoundException e) { </br> e.printStackTrace(); </br> } <span class="hljs-keyword">catch</span> (IOException e) { </br> <span class="hljs-comment">// TODO Auto-generated catch block </span></br> e.printStackTrace(); </br> } <span class="hljs-keyword">finally</span> { </br> <span class="hljs-keyword">try</span> { </br> raf.close(); </br> } <span class="hljs-keyword">catch</span> (Exception e) { </br> } </br> } </br> } </br> } </br> }</code></pre> </div>

相关文章
相关标签/搜索