【一】Apache commons IO包之IOUtils
前面我們已經學習了FileUtils,知道了Apache commons IO包提供了很多實用的工具來對文件進行操作。但是它們的底層到底是怎么實現的呢?如果現在我們要操作的不是文件,而是網絡資源呢?
其實FileUtils的基石就是IOUtils,它內置了大量的簡化方法來簡化IO讀寫操作和提供默認的緩沖支持。看看官網的說法:

【二】IOUtils的常用API及解析
①讀操作
IOUtils類提供的讀操作方法有兩大類:第一類是readLines方法。第二類是toXxx方法。
※ readLines方法
List readLines(InputStream input)
List readLines(InputStream input, String encoding)
readLines(Reader input)
我們知道在字節流中是沒有“行”的概念的,但是為什么這里的readLines方法可以接收InputStream呢?看看源代碼就知道了

public static List readLines(InputStream input, String encoding) throws IOException
{

if (encoding == null)
{
return readLines(input);

} else
{
InputStreamReader reader = new InputStreamReader(input, encoding);
return readLines(reader);
}
}


public static List readLines(Reader input) throws IOException
{
BufferedReader reader = new BufferedReader(input);
List list = new ArrayList();
String line = reader.readLine();

while (line != null)
{
list.add(line);
line = reader.readLine();
}
return list;
}
原來在底層,IOUtils使用了InputStreamReader對input stream進行了包裝,到了readLines(Reader)方法內,又再加了一個緩沖。如果我們是直接調用readLines(Reader)方法,為了確保編碼正確,需要手工創建一個InputStreamReader并指明encoding,否則將采用默認的encoding。
※ toXxx方法
IOUtils支持把input stream中的數據轉換成byte[],char[],String對象。而且input stream可以是字節流,字符流。同時可以指定encoding。這些方法實質上是“輸出”的過程:即從輸入流中讀入數據,然后轉換為byte[],char[],String,輸出到內存中。看看下面的一個源代碼:
public static char[] toCharArray(InputStream is, String encoding)

throws IOException
{
CharArrayWriter output = new CharArrayWriter();
copy(is, output, encoding);
return output.toCharArray();
}

public static void copy(InputStream input, Writer output, String encoding)

throws IOException
{

if (encoding == null)
{
copy(input, output);

} else
{
InputStreamReader in = new InputStreamReader(input, encoding);
copy(in, output);
}
}


public static int copy(Reader input, Writer output) throws IOException
{
long count = copyLarge(input, output);

if (count > Integer.MAX_VALUE)
{
return -1;
}
return (int) count;
}


public static long copyLarge(Reader input, Writer output) throws IOException
{
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;

while (-1 != (n = input.read(buffer)))
{
output.write(buffer, 0, n);
count += n;
}
return count;
}


我們可以看到這個過程是沒有進行flush的操作的,也就是說使用者必須負責在調用結束后進行緩存清空和輸入、輸入流關閉。對于input stream是文件的情況,在FileUtils的文件讀方法的最后都會調用IOUtils.closeQuietly(in);方法來確保輸入流正確關閉。
②寫操作
和讀操作一樣,IOUtils一樣提供了大量的寫方法,這些方法可以將byte[],char[],StringBuffer,String,Collection中的數據以字節流,字符流的形式寫入到目的源。
※ writeLines方法
public static void writeLines(Collection lines, String lineEnding,

OutputStream output, String encoding) throws IOException
{

if (encoding == null)
{
writeLines(lines, lineEnding, output);

} else
{

if (lines == null)
{
return;
}

if (lineEnding == null)
{
lineEnding = LINE_SEPARATOR;
}

for (Iterator it = lines.iterator(); it.hasNext(); )
{
Object line = it.next();

if (line != null)
{
output.write(line.toString().getBytes(encoding));
}
output.write(lineEnding.getBytes(encoding));
}
}
}

public static void writeLines(Collection lines, String lineEnding,
Writer writer) throws IOException
{

if (lines == null)
{
return;
}

if (lineEnding == null)
{
lineEnding = LINE_SEPARATOR;
}

for (Iterator it = lines.iterator(); it.hasNext(); )
{
Object line = it.next();

if (line != null)
{
writer.write(line.toString());
}
writer.write(lineEnding);
}
}
如果我們查看FileUtils,會發現它對所有的文件讀寫(包括writeLines,writeStringToFile),都是調用字節流+encoding的方式來進行的。因為所有基于字符流的方式最終都需要轉換為基于字節流的方式。
③流拷貝
我們在從文件等數據源讀入數據時,習慣性地以字節讀入,到了內存又轉換成String對象,最后修改性地以字符寫回文件。IOUtils提供了一系列方便的方法來進行這中間的轉換。
copy(InputStream input, Writer output, String encoding),這個方法使用指定的encoding,從字節流中讀入字節,然后按照encoding解碼,通過字符流寫回目的源。
copy(Reader input, OutputStream output, String encoding),這個方法從字符流中讀取字符,使用指定的encoding編碼,通過字節流寫回目的源,然后立即清空緩沖。
上面這兩個方法底層都調用了一個名為copyLarge的方法,他們分別在通過一個byte[]或者char[]數組對要寫回的內容進行緩沖。一次性地從源端讀入4K數據然后通過輸出流寫回。
public static long copyLarge(InputStream input, OutputStream output)

throws IOException
{
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;

while (-1 != (n = input.read(buffer)))
{
output.write(buffer, 0, n);
count += n;
}
return count;
}


public static long copyLarge(Reader input, Writer output) throws IOException
{
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;

while (-1 != (n = input.read(buffer)))
{
output.write(buffer, 0, n);
count += n;
}
return count;
}
④內容比較
這一點在前面FileUtils.contentEquals(File, File)方法中已經有提及,請參考上一篇文章
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
posted on 2010-03-08 21:24
Paul Lin 閱讀(2391)
評論(0) 編輯 收藏 所屬分類:
J2SE