在日常的工作中文件IO操作是一种非常常用的操作,本篇文章简单的介绍一下基本但很重要的File类和常用方法,以及对目录和文件的操作等等。
1、File类
File(文件)这个名称有一定的误导性,我们通常认为它指代的是文件,实际上却并非如此。它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。如果它指的是一个文件集,我们就可以对此集合调用list()方法,会返回一个字符数组,因为元素的个数是固定的。也许FilePath会更贴切一些。
1.1 目录列表
查看一个目录列表,可以使用两种方法来使用File对象。
1. 获得全部列表,不带参数的list()方法
2. 获得受限列表,使用目录过滤器
请看下列示例代码:
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;
public class DirList {
public static void main(final String[] args) {
File path = new File("D:\\DirTest");
String[] list;
if (args.length == 0)
list = path.list();
else
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(args[0]);
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for (String dirItem : list) {
System.out.println(dirItem);
}
}
}
此程序非常简单,列出目标文件夹下面的文件(文件夹名和文件名)。
特别注意,windows目录下路径分隔符为\\。
args[0]是什么呢?这是我们运行在命令行用java命令执行代码时输入的参数,这里我们将其作为正则表达式输入,当然在IDE(此处为Eclipse)中也是可以输入此参数的(点击菜单栏Run->Run Configurations),见下图。我们完全可以在程序中定义一个String来完成同样的操作,这里不需要纠结细节。
上面的代码中主要用到了
- FilenameFilter接口
它其中只有一个accept方法,目的在于使list()可以回调accept(),进而决定哪些文件包含在列表中。
即:list()方法会为此目录对象下的每个文件名调用accept(),来判断该文件是否包含在内。
这是一个策略模式的典型例子,list()实现了基本的功能,而且按照FilenameFilter的形式提供了这个策略,以便完善list()在提供服务时所需的算法。
- Pattern (final Class)
此类用于正则表达式的编译表示形式。指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。
常用方式如下:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
示例中a*b代表正则表达式,当然我们可以将其换为其它的正则表达式。关于正则表达式的详细用法,请查看其它文档,这里不做过多介绍。
例如在上面的code中,我们可以使用”M.*.java”来输出以M开头的java文件。关于更详细的类使用和介绍,请参看官方文档。
1.2 file的创建及检查
通过file类的常用方法我们可以完成一些对文件/文件夹的常用查询操作,见如下代码:
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MakeDir {
private static void usage() {
System.out.println();
}
private static void fileData(File f) {
System.out.println("abs path:" + f.getAbsolutePath() + "\n Can read :"
+ f.canRead() + "\n Can write :" + f.canWrite()
+ "\n get name :" + f.getName() + "\n get parent :"
+ f.getParent() + "\n get path :" + f.getPath() + "\n length :"
+ f.length() + "\n lastModified :" + f.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = sdf.format(new Date(f.lastModified()));
System.out.println("lastModified for read " + date);
if (f.isFile()) {
System.out.println("f is a file ");
} else if (f.isDirectory()) {
System.out.println("f is a directory ");
}
}
public static void main(String[] args) {
File file = new File("D:\\DirTest");
if (file.exists()) {
System.out.println(file + " exists\n");
} else {
file.mkdirs();
System.out.println(file + " created\n");
}
fileData(file);
}
}
输出如下:
D:\DirTest exists
abs path:D:\DirTest
Can read :true
Can write :true
get name :DirTest
get parent :D:\
get path :D:\DirTest
length :4096
lastModified :1480425237915
lastModified for read 2016-11-29 21:13:57
f is a directory
我们随便输入一个不存在的文件夹,结果如下:
D:\DirTest2 created
abs path:D:\DirTest2
Can read :true
Can write :true
get name :DirTest2
get parent :D:\
get path :D:\DirTest2
length :0
lastModified :1480427281934
lastModified for read 2016-11-29 21:48:01
f is a directory
可见它实现了我们想要的功能,在此基础上我们可以添加更高级的功能,比如删除某一个时间点后产生的文件,重命名文件等等。
1.3 循环获取目录下的特定文件
有了上面的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 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();
}
});
}
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>();
public Iterator<File> iterator() {
return files.iterator();
}
void addAll(TreeInfo other) {
files.addAll(other.files);
dirs.addAll(other.dirs);
}
public String toString() {
//使输出的格式便于查看,可暂时忽略
return "dirs: " + PPrint.pformat(dirs) + "\n\nfiles: "
+ PPrint.pformat(files);
}
}
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)
System.out.println(walk("D:\\Android"));
else
for (String arg : args)
System.out.println(walk(arg));
}
}
很容易发现,上面只是循环遍历给定的目录,并将其分别加入到两个列表中,然后输出到控制台。虽然实例很简单但是我们稍加扩展便可以实现其他的功能。
在本篇中我们用到了正则表达式的一些知识,但是并没有对其作深入的了解,仅仅使用了一些基础的用法。鉴于其在日常使用中应用广泛功能强大,所以在下一篇中我将会分享一些使用正则表达式的心得。