本文中例举了关于file操作的几个实例,包括DirList.java(显示目录下文件),Directory(显示当前目录或遍历目录结构的工具类)以及使用Directory的例子ProcessFile。
这些例子同步更新于我的github上:https://github.com/lovekun/JavaDemo 的JavaFile文件中
先来看第一个例子,关于File类的。
package file;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;
/*
* 该程序实现目录过滤器的功能
* 根据参数显示指定目录下所有文件
*/
public class DirList {
public static void main(String[] args) {
File path = new File("./src/file");
String[] list;
if(args.length == 0){
list = path.list();
}else{
//list回调DirFilter中的accept方法。
list = path.list(new DirFilter(args[0]));
}
//调用Arrays.sort的静态方法来对list中的String进行排序
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem);
}
}
class DirFilter implements FilenameFilter{
private Pattern pattern;
public DirFilter(String regex) {
pattern = Pattern.compile(regex);
}
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
}
File类的含义和Linux/Unix思想相似,目录也被看成是一种文件,java中对文件和目录也是这样处理的,File既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。
java的API文档中对File类的描述:http://docs.oracle.com/javase/7/docs/api/
这个例子展示的功能就是一个目录列表过滤器。DirFilter类继承了FilenameFilter接口,使list方法能够回调accept方法。
回调过程看下File类下的list方法源码:
/**
* Returns an array of strings naming the files and directories in the
* directory denoted by this abstract pathname that satisfy the specified
* filter. The behavior of this method is the same as that of the
* <code>{@link #list()}</code> method, except that the strings in the
* returned array must satisfy the filter. If the given
* <code>filter</code> is <code>null</code> then all names are accepted.
* Otherwise, a name satisfies the filter if and only if the value
* <code>true</code> results when the <code>{@link
* FilenameFilter#accept}</code> method of the filter is invoked on this
* abstract pathname and the name of a file or directory in the directory
* that it denotes.
*
* @param filter A filename filter
*
* @return An array of strings naming the files and directories in the
* directory denoted by this abstract pathname that were accepted
* by the given <code>filter</code>. The array will be empty if
* the directory is empty or if no names were accepted by the
* filter. Returns <code>null</code> if this abstract pathname
* does not denote a directory, or if an I/O error occurs.
*
* @throws SecurityException
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the directory
*/
public String[] list(FilenameFilter filter) {
String names[] = list();
if ((names == null) || (filter == null)) {
return names;
}
ArrayList v = new ArrayList();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
return (String[])(v.toArray(new String[v.size()]));
}
String names[] = list()调用了无参数的list()方法,把当前目录下所有文件的名字赋值给字符串数组names,然后判断如果names为空或者过滤内容(filter)为空,则返回该目录下所有文件。否则,便利目录下的所有文件,并为每个文件调用filter的accept方法,符合条件就把文件名加入到ArrayList中,并最终返回字符串数组。
再来看看FilenameFilter接口:
package java.io;
/**
* Instances of classes that implement this interface are used to
* filter filenames. These instances are used to filter directory
* listings in the <code>list</code> method of class
* <code>File</code>, and by the Abstract Window Toolkit's file
* dialog component.
*
* @author Arthur van Hoff
* @author Jonathan Payne
* @version %I%, %G%
* @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter)
* @see java.io.File
* @see java.io.File#list(java.io.FilenameFilter)
* @since JDK1.0
*/
public
interface FilenameFilter {
/**
* Tests if a specified file should be included in a file list.
*
* @param dir the directory in which the file was found.
* @param name the name of the file.
* @return <code>true</code> if and only if the name should be
* included in the file list; <code>false</code> otherwise.
*/
boolean accept(File dir, String name);
}
该接口只有一个方法accept来判断指定的文件能否被包含在file list中。
总结下上面的小例子:通过运行是传入的参数来对指定目录下的文件进行过滤输出。当然,你也可以把上面的DirFilter类写成内部类(具体可以参看《java编程思想》第18章Java IO系统)。这个例子比较难理解的感觉应该就在path.list(FilenameFilter)对accept()方法的回调。
直接来看第二个例子:也算是一个实用工具,可以在平时写代码时碰到关于目录遍历相关的内容时,直接拿过来用。
直接上代码:
package file;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/*
* ProcessFiles是一个目录工具类。
* 该代码实现的功能:
* 1、local()方法产生本地目录中的文件构成的File对象数组,
* 有两个不同参数的local()方法,一个传File,一个传String
* 2、walk()方法产生给定目录下的由整个目录树所有文件构成的List<File>。
*
*/
public class Directory {
//listFiles下采用匿名内部类的结构
public static File[] local(File dir, final String regex){
return dir.listFiles(new FilenameFilter(){
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name){
return pattern.matcher(new File(name).getName()).matches();
}
});
}
//重载local方法
public static File[] local(String path, final String regex){
return local(new File(path), regex);
}
public static class TreeInfo implements Iterable<File>{
public List<File> files = new ArrayList<File>();
public List<File> dirs = new ArrayList<File>();
@Override
public Iterator<File> iterator() {
return files.iterator();
}
void addAll(TreeInfo other){
files.addAll(other.files);
dirs.addAll(other.dirs);
}
//toString()方法是会自动调用的
//默认容器的toString会在一行把容器里所有元素全部打印
//所以这里重写了toString方法
public String toString(){
return "dirs: " + PPrint.pformat(dirs) + "\n\nfiles: " + PPrint.pformat(files);
}
}
/*
* 重载了四种walk()方法
*/
public static TreeInfo walk(String start, String regex){
return recurseDirs(new File(start),regex);
}
public static TreeInfo walk(File start, String regex){
return recurseDirs(start, regex);
}
public static TreeInfo walk(File start){
return recurseDirs(start, ".*");
}
public static TreeInfo walk(String start){
return recurseDirs(new File(start), ".*");
}
static TreeInfo recurseDirs(File startDir, String regex){
TreeInfo result = new TreeInfo();
for(File item : startDir.listFiles()){
if(item.isDirectory()){
result.dirs.add(item);
result.addAll(recurseDirs(item, regex));
}else{
if(item.getName().matches(regex)){
result.files.add(item);
}
}
}
return result;
}
public static void main(String[] args) {
if(args.length == 0){
//这里输出字符串,会自动调用TreeInfo的toString方法
System.out.println(walk("."));
}else{
for(String arg : args){
System.out.println(walk(arg));
}
}
}
}
代码细节,注释已经写的比较清楚了,两个主要的方法local()和walk().
local()方法实质上就是上一个例子的实用内部类的另一种写法,walk()方法会在指定目录下对该目录下所有文件进行遍历。
看看如何使用上面的工具类:
package file;
import java.io.File;
import java.io.IOException;
/*
* 根据提供的需要过滤的扩展名ext来输出当前
* 目录先所有符合条件的文件(不传入参数的时候)
*/
public class ProcessFiles {
public interface Strategy{
void process(File file);
}
private Strategy strategy;
private String ext;
//构造方法传入两个参数接口Strategy和需要过滤的文件扩展名
public ProcessFiles(Strategy strategy, String ext) {
this.strategy = strategy;
this.ext = ext;
}
public void start(String[] args){
try{
if(args.length == 0){
processDirectoryTree(new File("."));
}else{
for(String arg : args){
if(arg.endsWith("." + ext))
arg += "." + ext;
strategy.process(new File(arg).getCanonicalFile());
}
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
public void processDirectoryTree(File root) throws IOException{
for(File file : Directory.walk(root.getAbsolutePath(), ".*\\." + ext))
strategy.process(file.getCanonicalFile());
}
public static void main(String[] args) {
new ProcessFiles(new ProcessFiles.Strategy() {
@Override
public void process(File file) {
System.out.println(file);
}
},"java").start(args);
}
}