posts - 262,  comments - 221,  trackbacks - 0
【一】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 阅读(2365) 评论(0)  编辑  收藏 所属分类: J2SE

只有注册用户登录后才能发表评论。


网站导航:
 
<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(21)

随笔分类

随笔档案

BlogJava热点博客

好友博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜