http://groovy-lang.org/groovy-dev-kit.html
1.使用IO
Groovy提供了许多用于I / O 的 辅助方法。尽管您可以在Groovy中使用标准Java代码来处理这些代码,但Groovy提供了更便捷的方式来处理文件,流,读取器,...
您应该查看以下方法:
在
java.io.File
类:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html在
java.io.InputStream
类:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.htmljava.io.OutputStream
类:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.htmljava.io.Reader
课程:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.htmljava.io.Writer
课程:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.htmljava.nio.file.Path
类:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html
下一节重点介绍使用上面可用的辅助方法的示例惯用构造,但并不意味着对所有可用方法的完整描述。为此,请阅读GDK API。
1.1 读取文件
作为第一个示例,让我们看看如何在Groovy中打印文本文件的所有行:
new File(baseDir, 'haiku.txt').eachLine { line ->
println line
}
eachLine方法是FileGroovy自动添加到类的方法,并且具有许多变体,例如,如果您需要知道行号,则可以使用此变体:
new File(baseDir, 'haiku.txt').eachLine { line, nb ->
println "Line $nb: $line"
}
如果出于任何原因在eachLine主体中引发了异常,则该方法将确保正确关闭资源。对于Groovy添加的所有I / O资源方法,都是如此。
例如,在某些情况下,您将更喜欢使用Reader,但是仍然可以从Groovy的自动资源管理中受益。在下一个示例中,即使发生异常,Reader也会关闭:
def count = 0, MAXSIZE = 3
new File(baseDir,"haiku.txt").withReader { reader ->
while (reader.readLine()) {
if (++count > MAXSIZE) {
throw new RuntimeException('Haiku should only have 3 verses')
}
}
}
如果您需要将文本文件的行收集到列表中,则可以执行以下操作:
def list = new File(baseDir, 'haiku.txt').collect {it}
或者,您甚至可以利用as运算符将文件的内容分成几行:
def array = new File(baseDir, 'haiku.txt') as String[]
您需要将文件的内容放入byte[]多少次,并且需要多少代码?Groovy实际上非常容易:
byte[] contents = file.bytes
使用I / O不仅限于处理文件。实际上,很多操作都依赖于输入/输出流,因此,为什么Groovy会向这些流添加很多支持方法,如您在文档中所看到的 。
举个例子,你可以非常容易从File获取InputStream:
def is = new File(baseDir,'haiku.txt').newInputStream()
// do something ...
is.close()
但是,您可以看到它需要您关闭输入流。在Groovy中,通常最好使用withInputStream惯用语为您解决这个问题:
new File(baseDir,'haiku.txt').withInputStream { stream ->
// do something ...
}
1.2 写文件
当然,在某些情况下,您将不希望读取而是写入文件。选项之一是使用Writer:
new File(baseDir,'haiku.txt').withWriter('utf-8') { writer ->
writer.writeLine 'Into the ancient pond'
writer.writeLine 'A frog jumps'
writer.writeLine 'Water’s sound!'
}
但是对于这样一个简单的示例,使用<<运算符就足够了:
new File(baseDir,'haiku.txt') << '''Into the ancient pond
A frog jumps
Water’s sound!'''
当然,我们并不总是处理文本内容,因此您可以使用Writer或直接写字节,如本例所示:
file.bytes = [66,22,11]
当然,您也可以直接处理输出流。例如,以下是创建输出流以写入文件的方法:
def os = new File(baseDir,'data.bin').newOutputStream()
// do something ...
os.close()
但是,您会看到它需要您关闭输出流。同样,总的来说,使用withOutputStream惯用法来处理异常并在任何情况下关闭流都是一个更好的主意:
new File(baseDir,'data.bin').withOutputStream { stream ->
// do something ...
}
1.3 遍历文件树
在脚本编写上下文中,遍历文件树以查找某些特定文件并对其进行处理是一项常见的任务。Groovy提供了多种方法来执行此操作。例如,您可以对目录的所有文件执行某些操作:
// 对目录中找到的每个文件执行闭包代码
dir.eachFile { file ->
println file.name
}
// 对目录中与指定模式匹配的文件执行闭包代码
dir.eachFileMatch(~/.*\.txt/) { file ->
println file.name
}
通常,您将不得不处理更深层次的文件层次结构,在这种情况下,您可以使用eachFileRecurse:
// 递归地对目录中找到的每个文件或目录执行闭包代码
dir.eachFileRecurse { file ->
println file.name
}
// 仅对文件执行闭包代码,但递归执行
dir.eachFileRecurse(FileType.FILES) { file ->
println file.name
}
对于更复杂的遍历技术,可以使用traverse方法,该方法要求您设置一个特殊的标志来指示如何处理遍历:
dir.traverse { file ->
// 如果当前文件是目录,并且名称是bin,则停止遍历
if (file.directory && file.name=='bin') {
FileVisitResult.TERMINATE
} else {
// 否则,打印文件名并继续
println file.name
FileVisitResult.CONTINUE
}
}
1.4 数据与对象
在Java中,分别使用java.io.DataOutputStream和 java.io.DataInputStream类对数据进行序列化和反序列化并不少见。Groovy将使处理它们变得更加容易。例如,您可以使用以下代码将数据序列化为文件并反序列化:
boolean b = true
String message = 'Hello from Groovy'
// Serialize data into a file
file.withDataOutputStream { out ->
out.writeBoolean(b)
out.writeUTF(message)
}
// ...
// Then read it back
file.withDataInputStream { input ->
assert input.readBoolean() == b
assert input.readUTF() == message
}
同样,如果要序列化的数据实现了Serializable接口,则可以继续执行对象输出流,如下所示:
Person p = new Person(name:'Bob', age:76)
// Serialize data into a file
file.withObjectOutputStream { out ->
out.writeObject(p)
}
// ...
// Then read it back
file.withObjectInputStream { input ->
def p2 = input.readObject()
assert p2.name == p.name
assert p2.age == p.age
}
1.5 执行外部程序
上一节描述了在Groovy中处理文件,读取器或流有多么容易。但是,在诸如系统管理或开发人员之类的领域中,通常需要与外部流程进行通信。
Groovy提供了一种执行命令行过程的简单方法。只需将命令行写为字符串并调用该execute()方法即可。例如,在* nix机器(或安装了适当* nix命令的Windows机器)上,您可以执行以下命令:
def process = "ls -l".execute()
println "Found text ${process.text}"
该execute()方法返回一个java.lang.Process实例,该实例随后将允许处理输入/输出/错误流以及要检查来自进程的退出值等。
例如,这里是与上面相同的命令,但是我们现在一次处理一行结果:
def process = "ls -l".execute()
process.in.eachLine { line ->
println line
}
值得注意的是,in对应于命令的标准输出的输入流。out将指向一个流,您可以在其中将数据发送到流程(其标准输入)。
请记住,许多命令是Shell内置的,需要特殊处理。因此,如果要在Windows计算机上的目录中列出文件列表,请输入:
def process = "dir".execute()
println "${process.text}"
您将收到一条IOException谚语: 无法运行程序“ dir”:CreateProcess error = 2,系统找不到指定的文件。
这是因为它dir是Windows shell(cmd.exe)内置的,不能作为简单的可执行文件运行。相反,您将需要编写:
def process = "cmd /c dir".execute()
println "${process.text}"
同样,由于此功能当前使用 java.lang.Process,因此必须考虑该类的不足。特别是,此类的javadoc说:
由于某些本机平台仅为标准输入和输出流提供了有限的缓冲区大小,因此未能及时写入子流程的输入流或读取子流程的输出流可能导致子流程阻塞甚至死锁
因此,Groovy提供了一些附加的辅助方法,这些方法使处理流程的流变得更加容易。
这是如何吞噬过程中的所有输出(包括错误流输出)的方法:
def p = "rm -f foo.tmp".execute([], tmpDir)
p.consumeProcessOutput()
p.waitFor()
另外,这些是pipeTo命令(映射为| 允许重载),该命令可将一个进程的输出流馈送到另一个进程的输入流。
以下是一些使用示例:
proc1 = 'ls'.execute()
proc2 = 'tr -d o'.execute()
proc3 = 'tr -d e'.execute()
proc4 = 'tr -d i'.execute()
proc1 | proc2 | proc3 | proc4
proc4.waitFor()
if (proc4.exitValue()) {
println proc4.err.text
} else {
println proc4.text
}
消费错误输出
def sout = new StringBuilder()
def serr = new StringBuilder()
proc2 = 'tr -d o'.execute()
proc3 = 'tr -d e'.execute()
proc4 = 'tr -d i'.execute()
proc4.consumeProcessOutput(sout, serr)
proc2 | proc3 | proc4
[proc2, proc3].each { it.consumeProcessErrorStream(serr) }
proc2.withWriter { writer ->
writer << 'testfile.groovy'
}
proc4.waitForOrKill(1000)
println "Standard output: $sout"
println "Standard error: $serr"