文件迭代
我从未想过使用原始的 Java 代码逐行遍历 java.io.File。当我完成了所有的嵌套的 BufferedReader 和 FileReader 后(更别提每个流程末尾的所有异常处理),我已经忘记最初的目的是什么。
清单 18 展示了使用 Java 语言完成的整个过程:
清单 18. Java 文件迭代
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class WalkFile {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("languages.txt"));
String line = null;
while((line = br.readLine()) != null) {
System.out.println("I know " + line);
}
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
finally {
if(br != null) {
try {
br.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
}
}
清单 19 展示了 Groovy 中的等效过程:
清单 19. Groovy 文件迭代
f.eachLine{language->
println "I know ${language}"
}
这正是 Groovy 的简洁性真正擅长的方面。现在,我希望您了解为什么我将 Groovy 称为 “Java 程序员的 DSL”。
注意,我在 Groovy 和 Java 语言中同时处理同一个 java.io.File 类。如果该文件不存在,那么 Groovy 代码将抛出和 Java 代码相同的 FileNotFoundException 异常。区别在于,Groovy 没有已检测的异常。在 try/catch/finally 块中封装 eachLine() 结构是我自己的爱好 — 而不是一项语言需求。对于一个简单的命令行脚本中,我欣赏 清单 19 中的代码的简洁性。如果我在运行应用服务的同时执行相同的迭代,我不能对这些异常坐视不管。我将在与 Java 版本相同的 try/catch 块中封装 eachLine() 块。
File 类对 each() 方法进行了一些修改。其中之一就是 splitEachLine(String separator, Closure closure)。这意味着您不仅可以逐行遍历文件,同时还可以将它分为不同的标记。清单 20 展示了一个例子:
清单 20. 分解文件的每一行
// notice the space between the language and the version
Java 1.5
Groovy 1.6
JavaScript 1.x
// splitTest.groovy
def f = new File("languages.txt")
f.splitEachLine(" "){words->
words.each{ println it }
}
// output
Java
1.5
Groovy
1.6
JavaScript
1.x
如果处理的是二进制文件,Groovy 还提供了一个 eachByte() 方法。
当然,Java 语言中的 File 并不总是一个文件 — 有时是一个目录。Groovy 还提供了一些 each() 修改以处理子目录。