现在的位置: 首页 > 综合 > 正文

使用File类

2013年03月15日 ⁄ 综合 ⁄ 共 5887字 ⁄ 字号 评论关闭
文章目录

File 类

在介绍直接从流里读写数据的类之前,我们先介绍一下处理文件和目录的类。

File类有一个极具欺骗性的名字;或许你会认为这是一个关于文件的类,但它不是。你可以用它来表示某个文件的名字,也可以用它来表示目录里一组文件的名字。如果它表示的是一组文件,那么你还可以用list( )方法来进行查询,让它会返回String数组。由于元素数量是固定的,因此数组会比容器更好一些。如果你想要获取另一个目录的清单,再建一个File对象就是了。实际上,叫它 "FilePath"可能会更好一些。下面我们举例说明怎样使用这个类及其相关的FilenameFilter接口。

目录列表器

假设你想看看这个目录。有两个办法。一是不带参数调用list( )。它返回的是File对象所含内容的完整清单。但是,如果你要的是一个"限制性列表(restricted list)"的话 —— 比方说,你想看看所有扩展名为.java的文件 —— 那么你就得使用"目录过滤器"了。这是一个专门负责挑选显示File对象的内容的类。

下面就是源代码。看看,用了java.utils.Arrays.sort( )和11章的AlphabeticComparator之后,我们没费吹灰之力就对结果作了排序(按字母顺序):

//: c12:DirList.java
// Displays directory listing using regular expressions.
// {Args: "D.*/.java"}
import java.io.*;
import java.util.*;
import java.util.regex.*;
import com.bruceeckel.util.*;
public class DirList {
  public static void main(String[] args) {
    File path = new File(".");
    String[] list;
    if(args.length == 0)
      list = path.list();
    else
      list = path.list(new DirFilter(args[0]));
    Arrays.sort(list, new AlphabeticComparator());
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  }
}
class DirFilter implements FilenameFilter {
  private Pattern pattern;
  public DirFilter(String regex) {
    pattern = Pattern.compile(regex);
  }
  public boolean accept(File dir, String name) {
    // Strip path information, search for regex:
    return pattern.matcher(
      new File(name).getName()).matches();
  }
} ///:~

DirFilter实现了FilenameFilter接口。我们来看看FilenameFilter究竟有多简单:

public interface FilenameFilter {
  boolean accept(File dir, String name);
}

也就是说,这类对象的任务就是提供一个accept( )的方法。之所以要创建这个类,就是要给list( )提供一个accept( )方法,这样当list( )判断该返回哪些文件名的时候,能够"回过头来调用"accept( )方法。因此,这种结构通常被称为回调(callback)。更准确地说,由于list( )实现了基本功能,而FilenameFilter提供了"对外服务所需的算法",因此这是一种"策略模式(Strategy Pattern)"。由于list( )FilenameFilter对象当参数,因此你可以将任何实现FilenameFilter接口的对象传给它,并以此(甚至是在运行时)控制list( )的工作方式。回调能提高程序的灵活性。

DirFilter还告诉我们,interface只是包含了一些方法,它没说你只能写这些方法。(但是,你至少要定义接口里有的方法。) 这里我们还定义了DirFilter的构造函数。

accept( )方法需要两个参数,一个是File对象,表示这个文件是在哪个目录里面的;另一个是String,表示文件名。虽然你可以忽略它们中的一个,甚至两个都不管,但是你大概总得用一下文件名吧。记住,list( )会对目录里的每个文件调用accept( ),并以此判断是不是把它包括到返回值里;这个判断依据就是accept( )的返回值。

切记,文件名里不能有路径信息。为此你只要用一个String对象来创建File对象,然后再调用这个File对象的getName( )就可以了。它会帮你剥离路径信息(以一种平台无关的方式)。然后再在accept( )里面用正则表达式(regular expression)的matcher对象判断,regex是否与文件名相匹配。兜完这个圈子,list( )方法返回了一个数组。

匿名内部类

这是用匿名内部类(详见第八章)来重写程序的绝佳机会。下面我们先创建一个返回FilenameFilterfilter( )方法。

//: c12:DirList2.java
// Uses anonymous inner classes.
// {Args: "D.*/.java"}
import java.io.*;
import java.util.*;
import java.util.regex.*;
import com.bruceeckel.util.*;
public class DirList2 {
  public static FilenameFilter filter(final String regex) {
    // Creation of anonymous inner class:
    return new FilenameFilter() {
      private Pattern pattern = Pattern.compile(regex);
      public boolean accept(File dir, String name) {
        return pattern.matcher(
          new File(name).getName()).matches();
      }
    }; // End of anonymous inner class
  }
  public static void main(String[] args) {
    File path = new File(".");
    String[] list;
    if(args.length == 0)
      list = path.list();
    else
      list = path.list(filter(args[0]));
    Arrays.sort(list, new AlphabeticComparator());
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  }
} ///:~

注意,filter( )的参数必须是final的。要想在匿名内部类里使用其作用域之外的对象,只能这么做。

这是对前面所讲的代码的改进,现在FilenameFilter类已经与DirList2紧紧地绑在一起了。不过你还可以更进一步,把这个匿名内部类定义成list( )的参数,这样代码会变得更紧凑:

//: c12:DirList3.java
// Building the anonymous inner class "in-place."
// {Args: "D.*/.java"}
import java.io.*;
import java.util.*;
import java.util.regex.*;
import com.bruceeckel.util.*;
public class DirList3 {
  public static void main(final String[] args) {
    File path = new File(".");
    String[] list;
    if(args.length == 0)
      list = path.list();
    else
      list = path.list(new FilenameFilter() {
        private Pattern pattern = Pattern.compile(args[0]);
        public boolean accept(File dir, String name) {
          return pattern.matcher(
            new File(name).getName()).matches();
        }
      });
    Arrays.sort(list, new AlphabeticComparator());
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  }
} ///:~

现在该轮到main( )的参数成final了,因为匿名内部类要用它的arg[0]了。

这个例子告诉我们,可以用匿名内部类来创建专门供特定问题用的,一次性的类。这种做法的好处是,它能把解决某个问题的代码全都集中到一个地方。但是从另一角度来说,这样做会使代码的可读性变差,所以要慎重。

查看与创建目录

File类的功能不仅限于显示文件或目录。它还能帮你创建新的目录甚至是目录路径(directory path),如果目录不存在的话。此外它还能用来检查文件的属性(大小,上次修改的日期,读写权限等),判断File对象表示的是文件还是目录,以及删除文件。下面这段程序演示了File类的一些其他方法(请查阅JDK文档,以了解其全部功能):

//: c12:MakeDirectories.java
// Demonstrates the use of the File class to
// create directories and manipulate files.
// {Args: MakeDirectoriesTest}
import com.bruceeckel.simpletest.*;
import java.io.*;
public class MakeDirectories {
  private static Test monitor = new Test();
  private static void usage() {
    System.err.println(
      "Usage:MakeDirectories path1 .../n" +
      "Creates each path/n" +
      "Usage:MakeDirectories -d path1 .../n" +
      "Deletes each path/n" +
      "Usage:MakeDirectories -r path1 path2/n" +
      "Renames from path1 to path2");
    System.exit(1);
  }
  private static void fileData(File f) {
    System.out.println(
      "Absolute path: " + f.getAbsolutePath() +
      "/n Can read: " + f.canRead() +
      "/n Can write: " + f.canWrite() +
      "/n getName: " + f.getName() +
      "/n getParent: " + f.getParent() +
      "/n getPath: " + f.getPath() +
      "/n length: " + f.length() +
      "/n lastModified: " + f.lastModified());
    if(f.isFile())
      System.out.println("It's a file");
    else if(f.isDirectory())
      System.out.println("It's a directory");
  }
  public static void main(String[] args) {
    if(args.length < 1) usage();
    if(args[0].equals("-r")) {
      if(args.length != 3) usage();
      File
        old = new File(args[1]),
        rname = new File(args[2]);
      old.renameTo(rname);
      fileData(old);
      fileData(rname);
      return; // Exit main
    }
    int count = 0;
    boolean del = false;
    if(args[0].equals("-d")) {
      count++;
      del = true;
    }
    count--;
    while(++count < args.length) {
      File f = new File(args[count]);
      if(f.exists()) {
        System.out.println(f + " exists");
        if(del) {
          System.out.println("deleting..." + f);
          f.delete();
        }
      }
      else { // Doesn't exist
        if(!del) {
          f.mkdirs();
          System.out.println("created " + f);
        }
      }
      fileData(f);
    }
    if(args.length == 1 &&
        args[0].equals("MakeDirectoriesTest"))
      monitor.expect(new String[] {
        "%% (MakeDirectoriesTest exists"+
          "|created MakeDirectoriesTest)",
        "%% Absolute path: "
          + "//S+MakeDirectoriesTest",
        "%%  Can read: (true|false)",
        "%%  Can write: (true|false)",
        " getName: MakeDirectoriesTest",
        " getParent: null",
        " getPath: MakeDirectoriesTest",
        "%%  length: //d+",
        "%%  lastModified: //d+",
        "It's a directory"
      });
  }
} ///:~

在fileData( )演示了全套查询文件和目录路径信息的方法。

main( )的第一条指令就是执行renameTo( )。它会把文件重命名成(或者说移动到)新的目录,也就是参数所给出的目录。而参数本身就是一个File对象。这个方法也适用于目录。

如果你试过上面那段程序,就会发现,你能用它创建任意复杂的目录路径,因为mkdirs( )已经帮你打理好了。

抱歉!评论已关闭.