对于程序语言的设计者来说,创建一个好的输入输出系统是一个艰巨的任务
File类
File类这个名字有一定的误导性,我们可能会认为它指代的是文件,然而却并非如此,它既能表明一个文件特定的名称,又能代表一个目录下一组文件的名称,如果他指的是文件集,我们就可以对此集合调用list方法,会返回一个字符数组,也及时目录列表
假设我们想查看一个目录列表,可以用两种方法来使用File对象。如果我们调用不带参数的list0方法,便可以获得此File对象包含的全部列表。然而,如果我们想获得一个受限列表,例如,想得到所有扩展名为java的文件,那么我们就要用到“目录过滤器”,这个类会告诉我们怎样显示符合条件的File对象。
下面是一一个示例,注意,通过使用java.utils.Arrays.sort和String.CASE INSENSITIVE.ORDERComparator,可以很容易地对结果进行排序(按字母顺序)。
class DirFilter implements FilenameFilter { private Pattern p; public DirFilter(String regex) { p = Pattern.compile(regex); } @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub return p.matcher(name).matches(); } } class DirFilter2 { // 参数是final是必须的,这是匿名内部类必须的,这样才能使用来自该范围之外的对象 public static FilenameFilter filter(final String regex) { return new FilenameFilter() { private Pattern p = Pattern.compile(regex); @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub return p.matcher(name).matches(); } }; } } public class file { public static void main(final String[] args) { File path = new File("."); String[] list; if (args.length == 0) list = path.list(); else { System.out.println("false"); // 第一 list=path.list(new DirFilter(args[0]));//回调,,会调用accept方法 // 第二 // list=path.list(DirFilter2.filter(args[0])); // 第三,添加final,优点是将解决特定问题的代码隔离,聚拢于一点,但是却不易阅读,要谨慎使用 /*list = path.list(new FilenameFilter() { private Pattern p = Pattern.compile(args[0]); @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub return p.matcher(name).matches(); } });*/ } Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); for (String item : list) { System.out.println(item); } } /** * 得到指定路径下的文件列表 * * @param path */ public void getFileList(String path) { File f = new File(path); for (String name : f.list()) { System.out.println(name); } } /** * 得到当前的文件目录列表 */ @Test public void getFilelist() { File path = new File("."); for (File f : path.listFiles()) { if (f.isDirectory()) { System.out.println("文件夹:" + f.getName()); } else { System.out.println("记事本:" + f.getName()); } } } }
DirFiter这个类存在的唯一原因就是将accept方法。创建这个类的目的在于把accept0方法提供给listO使用,使list(可以回调accept,进而以决定哪些文件包含在列表中。因此,这种结构也常常称为回调。更具体地说,这是-一个策略模式的例子,因为list(实现了基本的功能,而且按照FilenameFilter的形式提供了这个策略,以便完善listO在提供服务时所需的算法。因为list(接受FilenameFilter对象作为参数,这意味着我们可以传递实现了FilenameFilter接口的任何类的对象,用以选择(甚至在运行时) list方 法的行为方式。策略的目的就是提供了代码行为的灵活性。
accept方法必须接受-一个代表某个特定文件所在目录的File对象,以及包含了那个文件名的一个String。记住一点: list方法会为此目录对象下的每个文件名调用accept,来判断该文件是否包含在内;判断结果由accept返回的布尔值表示。
accept会使用一个 正则表达式的matcher对象,来查看此正则表达式regex是否匹配这个文件的名字。通过使用accept, list方法会返回一个数组。
DirFilter2用了匿名内部类
这个例子很适合用一个匿名内部类进行改写。首先创建一个filter方法,它会返回一个指向FilenameFilter的引用注意,传向flter的参数必须是final的。这在匿名内部类中是必需的,这样它才能够使用来自该类范围之外的对象。这个设计有所改进,因为现在FilenameFilter类 紧密地和DirList2绑定在一起。然而,我们可以进一步修改该方法,定义一个作为list参数的匿名内部类;这样一来程序会变 得更小:比如在上的代码中main方法标注的第三部分中直接使用了匿名内部类,没有对别的对象产生依赖
既然匿名内部类直接使用args[0],那么传递给main方法的参数现在就是final的。
这个例子展示了匿名内部类怎样通过创建特定的、一次性的类来解决问题。此方法的一个优点就是将解决特定问题的代码隔离、聚拢于-一点。而另一方面,这种方法却不易阅读,因此要谨慎使用。
程序设计中一项常见的任务就是在文件集上执行操作,这些文件要么在本地目录中,要么遍布于整个目录树中。如果有一种工具能够为你产生这个文件集,那么它会非常有用。下面的实用工具类就可以通过使用local0方法产生由本地目录中的文件构成的File对象数组,或者通过使用walk0方法产生给定目录下的由整个目录树中所有文件构成的List<File> (File对象 比文件名更有用,因为File对象包含更多的信息)。这些文件是基于你提供的正则表达式而被选中的:
local0方法使用被称为listFileO的File.list(的变体来产生File数组。可以看到,它还使用了FilenameFilter。如果需要List而不是数组,你可以使用Arrays.asListO自己对结果进行转换。
walk方法将开始目录的名字转换为File对象,然后调用recurseDirs,该方法将递归地遍历目录,并在每次递归中都收集更多的信息。为了区分普通文件和目录,返回值实际上是一个对象“元组”一个List持有所有普通文件, 另一个持有目录。这里,所有的域都被有意识地设置成了public,因为TreeInfo的使命只是将对象收集起来一-如果你只是返回List, 那么就不需要将其设置为private,因为你只是返回一个对象对,不需要将它们设置为private。注意,TreeInfo实现了Iterable<File>,它将产生文件,使你拥有在文件列表上的“ 默认迭代”,而你可以通过声明“.dirs” 来指定目录。
TreeInfo.toString方法使用了一个“灵巧打印机”类,以使输出更容易浏览。容器默认的toString方法会在单个行中打印容器中的所有元素,对于大型集合来说,这会变得难以阅读,因此你可能希望使用可替换的格式化机制。下面是一个可以添加新行并缩排所有元素的工具:
pformat方法可以从Collection中产生格式化的String,而pprint0方 法使用pformat0来执行其任务。注意,没有任何元素和只有一个元素这两种特例进行了不同的处理。上面还有一 一个用于数组的pprint版本。
我们可以更进一步,创建一个工具,让他可以在目录中穿行,并且根据Strategy对象那个来处理哲学目录中的文件,这是策略设计模式的另一实例
Strategy接口内嵌在ProcessFiles中,使得如果你希望实现它,就必须实现ProcessFiles.Strategy,它为读者提供了更多的上下文信息。ProcessFiles执行 了查找具有特定扩展名(传递给构造器的ext参数)的文件所需的全部工作,并且当它找到匹配的文件时,将直接把文件传递给Strategy对象(也是传递给构造器的参数)。
如果你没有提供任何参数,那么ProcessFiles就假设你希望遍历当前目录下的所有目录。你也可以指定特定的文件,带不带扩展名都可以(如果必需的话,它会添加上扩展名),或者指定一个或多个目录。
在main方法中,你看到了如何使用这个工具的基本示例,它可以根据你提供的命令行来打印所有的Java源代码文件的名字。
这个例子展示了匿名内部类怎样通过创建特定的、一次性的类来解决问题。此方法的一个优点就是将解决特定问题的代码隔离、聚拢于-一点。而另一方面,这种方法却不易阅读,因此要谨慎使用。
程序设计中一项常见的任务就是在文件集上执行操作,这些文件要么在本地目录中,要么遍布于整个目录树中。如果有一种工具能够为你产生这个文件集,那么它会非常有用。下面的实用工具类就可以通过使用local0方法产生由本地目录中的文件构成的File对象数组,或者通过使用walk0方法产生给定目录下的由整个目录树中所有文件构成的List<File> (File对象 比文件名更有用,因为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; public final class Directory { public static File[] local(File f, final String regex) { return f.listFiles(new FilenameFilter() { private Pattern p = Pattern.compile(regex); @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub return p.matcher(name).matches(); } }); } 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() { // TODO Auto-generated method stub return files.iterator(); } void addAll(TreeInfo other) { files.addAll(other.files); files.addAll(other.dirs); } public String toString() { return "dir: " + PPrint.pFormat(dirs) + " " + "file: " + PPrint.pFormat(files); } } public static TreeInfo walk(File start, String regex) { return recurseDirs(start, regex); } public static TreeInfo walk(String start, String regex) { return recurseDirs(new File(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 f, String regex) { TreeInfo t = new TreeInfo(); for (File item : f.listFiles()) { if (item.isDirectory()) { t.dirs.add(item); t.addAll(recurseDirs(item, regex)); } else { if (item.getName().matches(regex)) t.files.add(item); } } return t; } public static void main(String[] args) { if (args.length == 0) { System.out.println(walk("E:\学道\HTML\Commons")); } else { for (String arg : args) { System.out.println(walk(arg)); } } } }
local0方法使用被称为listFileO的File.list(的变体来产生File数组。可以看到,它还使用了FilenameFilter。如果需要List而不是数组,你可以使用Arrays.asListO自己对结果进行转换。
walk方法将开始目录的名字转换为File对象,然后调用recurseDirs,该方法将递归地遍历目录,并在每次递归中都收集更多的信息。为了区分普通文件和目录,返回值实际上是一个对象“元组”一个List持有所有普通文件, 另一个持有目录。这里,所有的域都被有意识地设置成了public,因为TreeInfo的使命只是将对象收集起来一-如果你只是返回List, 那么就不需要将其设置为private,因为你只是返回一个对象对,不需要将它们设置为private。注意,TreeInfo实现了Iterable<File>,它将产生文件,使你拥有在文件列表上的“ 默认迭代”,而你可以通过声明“.dirs” 来指定目录。
TreeInfo.toString方法使用了一个“灵巧打印机”类,以使输出更容易浏览。容器默认的toString方法会在单个行中打印容器中的所有元素,对于大型集合来说,这会变得难以阅读,因此你可能希望使用可替换的格式化机制。下面是一个可以添加新行并缩排所有元素的工具:
import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; /** * 加上【】 * @author 11153 * */ public class PPrint { public static String pFormat(Collection<?> con) { if(con.size()==0) return "[]"; StringBuilder result=new StringBuilder("["); for(Object e:con) { if(con.size()!=1) result.append(" "); result.append(e); } if(con.size()!=1) { result.append(" "); } result.append("]"); return result.toString(); } public static void pprint(Object[] o) { System.out.println(pFormat(Arrays.asList(o))); } public static void pprint(List<File> dirs) { System.out.println(pFormat(dirs)); } public static void main(String[] args) { PPrint.pprint(Directory.walk("E:\学道\HTML\编程全解").dirs); PPrint.pprint(Directory.walk("E:\学道\HTML\编程全解").files); } }
pformat方法可以从Collection中产生格式化的String,而pprint0方 法使用pformat0来执行其任务。注意,没有任何元素和只有一个元素这两种特例进行了不同的处理。上面还有一 一个用于数组的pprint版本。
我们可以更进一步,创建一个工具,让他可以在目录中穿行,并且根据Strategy对象那个来处理哲学目录中的文件,这是策略设计模式的另一实例
package File; import java.io.File; import java.io.IOException; public class ProcessFiles { public interface Strategy { void process(File f); } private String ext; private Strategy s; public ProcessFiles(Strategy s,String ext){ this.s=s; this.ext=ext; } public void start(String[] args) { try { if (args.length == 0) { processDirectoryTree(new File(".")); } else { for (String arg : args) { File f = new File(arg); if (f.isDirectory()) processDirectoryTree(f); else { if (!arg.endsWith("." + ext)) arg += "." + ext; s.process(new File(arg).getCanonicalFile()); } } } } catch (IOException e) { } } public void processDirectoryTree(File root) throws IOException { for (File f : Directory.walk(root.getAbsolutePath(), ".*\." + ext)) s.process(f.getCanonicalFile()); } public static void main(String[] args) { new ProcessFiles(new ProcessFiles.Strategy() { public void process(File f) { System.out.println(f); } },"java").start(args);; } }
Strategy接口内嵌在ProcessFiles中,使得如果你希望实现它,就必须实现ProcessFiles.Strategy,它为读者提供了更多的上下文信息。ProcessFiles执行 了查找具有特定扩展名(传递给构造器的ext参数)的文件所需的全部工作,并且当它找到匹配的文件时,将直接把文件传递给Strategy对象(也是传递给构造器的参数)。
如果你没有提供任何参数,那么ProcessFiles就假设你希望遍历当前目录下的所有目录。你也可以指定特定的文件,带不带扩展名都可以(如果必需的话,它会添加上扩展名),或者指定一个或多个目录。
在main方法中,你看到了如何使用这个工具的基本示例,它可以根据你提供的命令行来打印所有的Java源代码文件的名字。