摘要
Apache Commons IO是Apache基金会创建并维护的Java函数库。
它提供了许多类使得开发者的常见任务变得简单,同时减少重复(boiler-plate)代码,这些代码可能遍布于每个独立的项目中,你却不得不重复的编写。这些类由经验丰富的开发者维护,对各种问题的边界条件考虑周到,并持续修复相关bug。
Commons IO是针对开发IO流功能的工具类库。
主要包括六个区域:
- 工具类——使用静态方法执行共同任务
- 输入——用于InputStream和Reader实现
- 输出——用于OutputStream和Writer实现
- 过滤器——各种文件过滤器实现
- 比较器——各种文件的java.util.Comparator实现
- 文件监听器——监听文件系统事件的组件
使用实例
maven依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
工具类
IOUtils
IOUtils包含处理读、写和复制的工具方法。方法对InputStream、OutputStream、Reader和Writer起作用。
从一个URL读取字节的任务,并且打印它们
常用JAVA操作
InputStream in = new URL( "http://commons.apache.org" ).openStream();
try {
InputStreamReader inR = new InputStreamReader( in );
BufferedReader buf = new BufferedReader( inR );
String line;
while ( ( line = buf.readLine() ) != null ) {
System.out.println( line );
}
} finally {
in.close();
}
IOUtils操作
InputStream in = new URL( "http://commons.apache.org" ).openStream();
try {
System.out.println(IOUtils.toString(in));
} finally {
IOUtils.closeQuietly(in);
}
在某些应用领域,这些IO操作是常见的,而这个类可以节省大量的时间。你可以依靠经过良好测试的代码。这样的实用程序代码,灵活性和速度是最重要的。但是你也应该理解这种方法的局限性。使用上述技术读取一个1 gb文件将导致试图创建一个1 gb的字符串对象!
FileUtils
FileUtils类包含使用File对象的工具方法。包括读写、复制和比较稳健。
读取整个文件行
File file = new File("/commons/io/project.properties");
List lines = FileUtils.readLines(file, "UTF-8");
FilenameUtils
FilenameUtils类包含工具方法不需要使用File对象就可以操作文件名。该类致力于屏蔽Unix和Windows之间的不同,避免这些环境之间的转换(例如,从开发到生产)。
规范文件删除双点片段
String filename = "C:/commons/io/../lang/project.xml";
FileSystemUtils
FileSystemUtils类包含使用JDK不支持的文件系统访问功能的工具方法。当前,只有获取驱动的空间大小的方法。注意,这是使用的命令行,而不是本地代码。
long freeSpace = FileSystemUtils.freeSpace("C:/");
Endian类
不同的计算机体系采用不同的字节排序约定。在所谓的“Little Endian”的体系结构中(例如Intel),低位字节存储在内存中较低地址,后续字节在较高地址。对于“Big EndIan”体系结构,(例如Motorola),情况恰好相反。
在关联包中有两个类:
EndianUtils类包含交换Java原始和流的Endian-ness的静态方法。
SwappedDataInputStream类是DataInput接口的实现。使用它,我们能从非本地EndIan-ness的文件读取数据。
行迭代器
org.apache.commons.io.LineIterator类提供灵活的方式使用一个基于行的文件。可以直接,或通过FileUtils或IOUtils的工厂方法创建实例。推荐使用模式:
//处理行
} finally {
LineIterator.closeQuietly(iterator);
}
文件比较器
org.apache.commons.io.comparator包提供一系列java.io.File的java.util.Comparator实现。这些比较器能用于排序文件列表和数组。
流
org.apache.commons.io.input和org.apache.commons.io.output包含各种有用的流实现。
- 空输出流——默默的吸收发给它的所有数据。
- Tee输出流——发送输出数据到两个流。
- 字节输出输出流——更便捷的JDK类版本。
- 计算流——统计传递的字节数。
- 代理流——委托恰当的方法代理。
- 可锁定的Writer——使用文件锁提供同步的Writer。
实例
java.io.File
通常,你必须使用文件和文件名。有很多事情可能出错:
- 一个类可以在Unix上工作但不能在Windows上工作(反之亦然)
- 由于双重或丢失路径分隔符的无效路径
- UNC文件名(在Windows上)不使用我的本地文件名功能函数
- 等等
这些都是很好的理由不使用文件名作为字符串。使用java.io.File而不是处理上面的很多中情况。因此,我们最好的实践推荐使用java.io.File而不是文件名字符串避免平台依赖。
Commons-io 1.1包括一个专注于文件名处理的类——FilenameUtils。这处理许多这些文件名问题,然而,任然推荐,尽可能,使用java.io.File对象。
public static String getExtension(String filename) {
int index = filename.lastIndexOf('.');
if (index == -1) {
return "";
} else {
return filename.substring(index + 1);
}
}
足够简单?对的,但如果传入一个完整路径而不是一个文件名会发生什么?请看下面,完全合法的路径:“C:\Temp\documentation.new\README”。定义在上面的方法返回“new\README”,肯定不是你想要的。
请使用java.io.File而不是字符串。在FileUtils你将看到围绕java.io.File的功能函数。
不推荐:
String tmpdir = "/var/tmp";
String tmpfile = tmpdir + System.getProperty("file.separator") + "test.tmp";
InputStream in = new java.io.FileInputStream(tmpfile);
推荐:
File tmpdir = new File("/var/tmp");
File tmpfile = new File(tmpdir, "test.tmp");
InputStream in = new java.io.FileInputStream(tmpfile);
缓冲流
IO性能依赖于缓冲策略。通常,读取大小为512或1024字节的数据包速度很快,因为这些大小匹配使用在硬盘上的文件系 统或文件系统缓冲中的数据包大小。但只要你多次只读取几个字节,性能肯定下降。
当你读取或输出流尤其是处理文件时确保你正确的缓冲流。只是使用BufferedInputStream装饰你的FileInputStream。
InputStream in = new java.io.FileInputStream(myfile);
try {
in = new java.io.BufferedInputStream(in);
in.read(.....
} finally {
IOUtils.closeQuietly(in);
}
注意,不要缓冲已经缓冲的流。一些组件像XML解析器可以做自己的缓冲,因此不需要装饰InputStream传入XML解析器,但减慢你的代码。如果你使用CopyUtils或IOUtils不需要使用额外的缓冲流
获取文件信息
System.out.println("Full path of exampleTxt: " + FilenameUtils.getFullPath(EXAMPLE_TXT_PATH));
System.out.println("Full name of exampleTxt: " + FilenameUtils.getName(EXAMPLE_TXT_PATH));
System.out.println("Extension of exampleTxt: " + FilenameUtils.getExtension(EXAMPLE_TXT_PATH));
System.out.println("Base name of exampleTxt: " + FilenameUtils.getBaseName(EXAMPLE_TXT_PATH));
FileEntry entry = new FileEntry(FileUtils.getFile(EXAMPLE_PATH));
System.out.println("File monitored: " + entry.getFile());
System.out.println("File name: " + entry.getName());
System.out.println("Is the file a directory?: " + entry.isDirectory());
判断父文件夹中是否包含指定子文件
File parent = FileUtils.getFile(PARENT_DIR);
System.out.println("Parent directory contains exampleTxt file: " +
FileUtils.directoryContains(parent, exampleFile));
字符比较操作
String str1 = "This is a new String.";
String str2 = "This is another new String, yes!";
System.out.println("Ends with string (case sensitive): " +
IOCase.SENSITIVE.checkEndsWith(str1, "string."));
System.out.println("Ends with string (case insensitive): " +
IOCase.INSENSITIVE.checkEndsWith(str1, "string."));
System.out.println("String equality: " +
IOCase.SENSITIVE.checkEquals(str1, str2));
按行读文件
File exampleFile = FileUtils.getFile(EXAMPLE_TXT_PATH);
LineIterator iter = FileUtils.lineIterator(exampleFile);
System.out.println("Contents of exampleTxt...");
while (iter.hasNext()) {
System.out.println("\t" + iter.next());
}
iter.close();
文件监视
File parentDir = FileUtils.getFile(PARENT_DIR);
FileAlterationObserver observer = new FileAlterationObserver(parentDir);
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println("File created: " + file.getName());
}
@Override
public void onFileDelete(File file) {
System.out.println("File deleted: " + file.getName());
}
@Override
public void onDirectoryCreate(File dir) {
System.out.println("Directory created: " + dir.getName());
}
@Override
public void onDirectoryDelete(File dir) {
System.out.println("Directory deleted: " + dir.getName());
}
});
FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer); // 单位: ms
try {
monitor.start();
// After we attached the monitor, we can create some files and directories
// and see what happens!
File newDir = new File(NEW_DIR);
File newFile = new File(NEW_FILE);
newDir.mkdirs();
newFile.createNewFile();
Thread.sleep(1000);
FileDeleteStrategy.NORMAL.delete(newDir);
FileDeleteStrategy.NORMAL.delete(newFile);
Thread.sleep(1000);
monitor.stop();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
文件过滤
File dir = FileUtils.getFile(PARENT_DIR);
String[] acceptedNames = {"example", "exampleTxt.txt"};
for (String file: dir.list(new NameFileFilter(acceptedNames, IOCase.INSENSITIVE))) {
System.out.println("File found, named: " + file);
}
通配符过滤
File dir = FileUtils.getFile(PARENT_DIR);
for (String file: dir.list(new WildcardFileFilter("*ample*"))) {
System.out.println("Wildcard file found, named: " + file);
}
前缀过滤
File dir = FileUtils.getFile(PARENT_DIR);
for (String file: dir.list(new PrefixFileFilter("example"))) {
System.out.println("Prefix file found, named: " + file);
}
后缀过滤
File dir = FileUtils.getFile(PARENT_DIR);
for (String file: dir.list(new OrFileFilter(
new WildcardFileFilter("*ample*"), new SuffixFileFilter(".txt")))) {
System.out.println("Or file found, named: " + file);
}
包含和去除同时过滤
File dir = FileUtils.getFile(PARENT_DIR);
for (String file: dir.list(new AndFileFilter(
new WildcardFileFilter("*ample*"),
new NotFileFilter(new SuffixFileFilter(".txt"))))) {
System.out.println("And/Not file found, named: " + file);
}
输入流转输出流
TeeInputStream tee = null;
try {
ByteArrayInputStream in = new ByteArrayInputStream(INPUT.getBytes("US-ASCII"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
tee = new TeeInputStream(in, out, true);
tee.read(new byte[INPUT.length()]);
System.out.println("Output stream: " + out.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try { tee.close(); }
catch (IOException e) { e.printStackTrace(); }
}
输入流转多个输出流
TeeInputStream teeIn = null;
TeeOutputStream teeOut = null;
try {
// TeeOutputStream
ByteArrayInputStream in = new ByteArrayInputStream(INPUT.getBytes("US-ASCII"));
ByteArrayOutputStream out1 = new ByteArrayOutputStream();
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
teeOut = new TeeOutputStream(out1, out2);
teeIn = new TeeInputStream(in, teeOut, true);
teeIn.read(new byte[INPUT.length()]);
System.out.println("Output stream 1: " + out1.toString());
System.out.println("Output stream 2: " + out2.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try { teeIn.close(); }
catch (IOException e) { e.printStackTrace(); }
}
文件排序
File parentDir = FileUtils.getFile(PARENT_DIR);
NameFileComparator comparator = new NameFileComparator(IOCase.SENSITIVE);
File[] sortedFiles = comparator.sort(parentDir.listFiles());
System.out.println("Sorted by name files in parent directory: ");
for (File file: sortedFiles) {
System.out.println("\t"+ file.getAbsolutePath());
}
文件大小排序
SizeFileComparator sizeComparator = new SizeFileComparator(true);
File[] sizeFiles = sizeComparator.sort(parentDir.listFiles());
System.out.println("Sorted by size files in parent directory: ");
for (File file: sizeFiles) {
System.out.println("\t"+ file.getName() + " with size (kb): " + file.length());
}
文件修改时间排序
LastModifiedFileComparator lastModified = new LastModifiedFileComparator();
File[] lastModifiedFiles = lastModified.sort(parentDir.listFiles());
System.out.println("Sorted by last modified files in parent directory: ");
for (File file: lastModifiedFiles) {
Date modified = new Date(file.lastModified());
System.out.println("\t"+ file.getName() + " last modified on: " + modified);
}
获取指定两个文件修改时间
File file1 = FileUtils.getFile(FILE_1);
File file2 = FileUtils.getFile(FILE_2);
if (lastModified.compare(file1, file2) > 0)
System.out.println("File " + file1.getName() + " was modified last because...");
else
System.out.println("File " + file2.getName() + "was modified last because...");
System.out.println("\t"+ file1.getName() + " last modified on: " +
new Date(file1.lastModified()));
System.out.println("\t"+ file2.getName() + " last modified on: " +
new Date(file2.lastModified()));