Java IO 檔案拷貝功能的實現
如果要想實現檔案的拷貝操作,有以下兩種方法:
方法1、將所有檔案的內容一次性讀取到程式之中,然後一次性輸出;這樣的話就需要開啟一個跟檔案一樣大小的資料用於臨時儲存這些資料,但是當檔案過大的時候呢?程式是不是會崩掉呢?歡迎大家踴躍嘗試^@^。
方法2、採用邊讀邊寫的操作,這樣一來效率也提高了,也不會佔用過多的記憶體空間。
所以,我們採用第二種方法,邊讀邊寫。
package signal.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class CopyFile {
/**
* 定義一個方法來實現拷貝檔案的功能
*
* @param src 源檔案路徑
* @param target 目標檔案路徑
* @throws IOException
*/
public static void CopyFile(File src, File target) throws IOException{
/**
* 驗證源檔案是否存在
*/
if(!src.exists()){
System.err.println("There is no such a file !!!");
System.exit(1);
}
/**
* 驗證目標路徑是否存在,不存在的話建立父路徑
*/
if(!target.getParentFile().exists()){
target.getParentFile().mkdirs();
}
int temp = 0; //用作標示是否讀取到源檔案最後
InputStream input = new FileInputStream(src);
OutputStream output = new FileOutputStream(target);
while((temp = input.read()) != -1){
output.write(temp);
}
input.close();
output.close();
}
public static void main(String[] args) {
String in = "C:\\Users\\Administrator\\Desktop\\tmp.txt";//源檔案路徑
String out = "C:\\Users\\Administrator\\Desktop\\tmp_copy.txt";//目標路徑(此時還可以重新命名)
File src = new File(in);
File target = new File(out);
try {
CopyFile(src, target);
System.out.println("Copy Successfully !");
} catch (IOException e) {
System.err.println("ERROR: Something wrong while copying !");
e.printStackTrace();
}
}
}
正如代碼中寫的那樣,我們是一邊讀取一邊寫入。使用到的 read() 方法源碼如下:
...
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* <p> A subclass must provide an implementation of this method.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
*/
public abstract int read() throws IOException;
...
裡面寫到,”@return the next byte of data, or -1 if the end of the stream is reached.”,read() 方法的傳回值為 “-1” 時表示檔案讀取完畢。這樣一來就不難解釋:
while((temp = input.read()) != -1){
output.write(temp);
}
有了這個基礎,我們就可以再稍微改進一下,不要一次一個位元組一個位元組的讀取和寫入了。版本2如下:
package signal.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class CopyFileVersion2 {
/**
* 定義一個方法來實現拷貝檔案的功能
*
* @param src 源檔案路徑
* @param target 目標檔案路徑
* @throws IOException
*/
public static void CopyFile(File src, File target) throws IOException{
/**
* 驗證源檔案是否存在
*/
if(!src.exists()){
System.err.println("There is no such a file !!!");
System.exit(1);
}
/**
* 驗證目標路徑是否存在,不存在的話建立父路徑
*/
if(!target.getParentFile().exists()){
target.getParentFile().mkdirs();
}
int temp = 0;
byte data[] = new byte[1024]; //每次讀取1024位元組
InputStream input = new FileInputStream(src);
OutputStream output = new FileOutputStream(target);
while((temp = input.read(data)) != -1){
output.write(data,0,temp);
}
input.close();
output.close();
}
public static void main(String[] args) {
String in = "C:\\Users\\Administrator\\Desktop\\tmp.txt";
String out = "C:\\Users\\Administrator\\Desktop\\tmp_copy.txt";
File src = new File(in);
File target = new File(out);
try {
long start = System.currentTimeMillis();
CopyFile(src, target);
long end = System.currentTimeMillis();
System.out.println("Copy Successfully! \nAnd it costs us " + (end - start) + " milliseconds.");
} catch (IOException e) {
System.err.println("ERROR: Something wrong while copying !");
e.printStackTrace();
}
}
}
很明顯可以看出不同,這裡我們定義了一個1024位元組的數組,這個數組的大小由自己來決定,其實這就是與第一種方法的結合。
這裡我們用到 read() 和 write() 兩個方法也跟之前不同了,是帶參數的。
read( byte b[] ) 源碼如下:
/**
* Reads some number of bytes from the input stream and stores them into
* the buffer array <code>b</code>. The number of bytes actually read is
* returned as an integer. This method blocks until input data is
* available, end of file is detected, or an exception is thrown.
*
* <p> If the length of <code>b</code> is zero, then no bytes are read and
* <code>0</code> is returned; otherwise, there is an attempt to read at
* least one byte. If no byte is available because the stream is at the
* end of the file, the value <code>-1</code> is returned; otherwise, at
* least one byte is read and stored into <code>b</code>.
*
* <p> The first byte read is stored into element <code>b[0]</code>, the
* next one into <code>b[1]</code>, and so on. The number of bytes read is,
* at most, equal to the length of <code>b</code>. Let <i>k</i> be the
* number of bytes actually read; these bytes will be stored in elements
* <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
* leaving elements <code>b[</code><i>k</i><code>]</code> through
* <code>b[b.length-1]</code> unaffected.
*
* <p> The <code>read(b)</code> method for class <code>InputStream</code>
* has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception IOException If the first byte cannot be read for any reason
* other than the end of the file, if the input stream has been closed, or
* if some other I/O error occurs.
* @exception NullPointerException if <code>b</code> is <code>null</code>.
* @see java.io.InputStream#read(byte[], int, int)
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
源碼已經很清楚的寫出來了,”@param b the buffer into which the data is read.”,參數是要讀取到記憶體中的位元組數組,如果再跟下去,我們發現,其實這個方法是調用了 read( byte b[] , int off , int len )
其實最後,還是使用的 read() 方法,我們只不過是調用了已經封裝好的一些方法。
對於 write( byte b[], int off, int len )方法,源碼如下:
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
* The general contract for <code>write(b, off, len)</code> is that
* some of the bytes in the array <code>b</code> are written to the
* output stream in order; element <code>b[off]</code> is the first
* byte written and <code>b[off+len-1]</code> is the last byte written
* by this operation.
* <p>
* The <code>write</code> method of <code>OutputStream</code> calls
* the write method of one argument on each of the bytes to be
* written out. Subclasses are encouraged to override this method and
* provide a more efficient implementation.
* <p>
* If <code>b</code> is <code>null</code>, a
* <code>NullPointerException</code> is thrown.
* <p>
* If <code>off</code> is negative, or <code>len</code> is negative, or
* <code>off+len</code> is greater than the length of the array
* <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @exception IOException if an I/O error occurs. In particular,
* an <code>IOException</code> is thrown if the output
* stream is closed.
*/
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
同樣的,這個方法也是對 write( int b ) 方法的封裝。
另外我加了可以測試耗費時間的一個小功能,原理很簡單,就是使用 currentTimeMillis() 方法,源碼如下:
/**
* Returns the current time in milliseconds. Note that
* while the unit of time of the return value is a millisecond,
* the granularity of the value depends on the underlying
* operating system and may be larger. For example, many
* operating systems measure time in units of tens of
* milliseconds.
*
* <p> See the description of the class <code>Date</code> for
* a discussion of slight discrepancies that may arise between
* "computer time" and coordinated universal time (UTC).
*
* @return the difference, measured in milliseconds, between
* the current time and midnight, January 1, 1970 UTC.
* @see java.util.Date
*/
public static native long currentTimeMillis();
可以看到,這個方法的傳回值是目前時間到1970年1月1日的間隔,單位是毫秒。
到此我們簡單的檔案拷貝功能就實現了,其實就是對於IO的一個實際應用。大家在學習的過程中可以追進源碼去看,很多什麼參數呀傳回值什麼的介紹的都很詳細。用了這麼長時間的Eclipse,是java程式員的真愛,代碼提示的功能很給力,就怕有一天離開了Eclipse連”Hello World”都寫不出來了^_^ 。相比大家在學習IO的時候,要麼是看視頻,要麼是學院派,老師肯定會跟你說IO有多重要多重要,一定要學好,所以大家在學習的過程中不妨寫幾個小功能,以便於對IO更好的掌握。這次我只是一時興起,不代表這就是最終的實現,大家可以結合自己學到東西,隨意改My Code,但是別忘了自己的目的。
本文永久更新連結地址:https://www.bkjia.com/Linux/2018-03/151440.htm