Groovy 提供了许多使用 I/O 的 辅助方法。虽然您可以在 Groovy 中使用标准 Java 代码来处理这些问题,但 Groovy 提供了更方便的方法来处理文件、流、阅读器……
特别是,您应该查看添加到的方法:

以下部分重点介绍使用上述可用辅助方法的示例惯用构造,但并不意味着对所有可用方法进行完整描述。为此,请阅读GDK API

1.读取文件

作为第一个示例,让我们看看如何在 Groovy 中打印文本文件的所有行:

  1. new File(baseDir, 'haiku.txt').eachLine { line ->
  2. println line
  3. }

eachLine方法是Groovy 自动添加到File类中的一个方法,有很多变体,例如如果你需要知道行号,你可以使用这个变体:

  1. new File(baseDir, 'haiku.txt').eachLine { line, nb ->
  2. println "Line $nb: $line"
  3. }

如果由于某种原因在eachLine主体中引发异常,该方法会确保资源已正确关闭。这适用于 Groovy 添加的所有 I/O 资源方法。

例如,在某些情况下,您会更喜欢使用Reader.,但仍然可以从 Groovy 的自动资源管理中受益。在下一个示例中,即使发生异常,阅读器也会关闭:

  1. def count = 0, MAXSIZE = 3
  2. new File(baseDir,"haiku.txt").withReader { reader ->
  3. while (reader.readLine()) {
  4. if (++count > MAXSIZE) {
  5. throw new RuntimeException('Haiku should only have 3 verses')
  6. }
  7. }
  8. }

如果您需要将文本文件的行收集到列表中,您可以执行以下操作:

  1. def list = new File(baseDir, 'haiku.txt').collect {it}

或者您甚至可以利用as运算符将文件的内容放入行数组中:

  1. def array = new File(baseDir, 'haiku.txt') as String[]

您需要将文件内容放入byte[]中多少次,它需要多少代码?Groovy实际上非常简单:

  1. byte[] contents = file.bytes

使用 I/O 不仅限于处理文件。事实上,很多操作都依赖于输入/输出流,因此 Groovy 为它们添加了很多支持方法,正如您在 文档中看到的那样。

例如,您可以很容易地从一个File中获得一个InputStream

  1. def is = new File(baseDir,'haiku.txt').newInputStream()
  2. // do something ...
  3. is.close()

但是您可以看到它需要您处理关闭输入流。在 Groovy 中,通常使用withInputStream可以为您解决这个问题:

  1. new File(baseDir,'haiku.txt').withInputStream { stream ->
  2. // do something ...
  3. }

2.写入文件

当然,在某些情况下,您不想读取而是写入文件。一种选择是使用Writer

  1. new File(baseDir,'haiku.txt').withWriter('utf-8') { writer ->
  2. writer.writeLine 'Into the ancient pond'
  3. writer.writeLine 'A frog jumps'
  4. writer.writeLine 'Water’s sound!'
  5. }

但是对于这样一个简单的示例,使用<<运算符就足够了:

  1. new File(baseDir,'haiku.txt') << '''Into the ancient pond
  2. A frog jumps
  3. Water’s sound!'''

当然,我们并不总是处理文本内容,因此您可以使用Writer或直接写入字节,如下例所示:

  1. file.bytes = [66,22,11]

当然你也可以直接处理输出流。例如,以下是创建输出流以写入文件的方法:

  1. def os = new File(baseDir,'data.bin').newOutputStream()
  2. // do something ...
  3. os.close()

但是您可以看到它需要您处理关闭输出流。同样,withOutputStream在任何情况下,使用将处理异常并关闭流的习语通常是一个更好的主意:

  1. new File(baseDir,'data.bin').withOutputStream { stream ->
  2. // do something ...
  3. }

3.遍历文件树

在脚本上下文中,遍历文件树以查找某些特定文件并对其进行处理是一项常见任务。Groovy 提供了多种方法来执行此操作。例如,您可以对目录的所有文件执行某些操作:

  1. // 对目录中找到的每个文件执行关闭代码
  2. dir.eachFile { file ->
  3. println file.name
  4. }
  5. // 对与指定模式匹配的目录中的文件执行关闭代码
  6. dir.eachFileMatch(~/.*\.txt/) { file ->
  7. println file.name
  8. }

通常,您必须处理更深层次的文件,在这种情况下,您可以使用eachFileRecurse:

  1. // 递归地对目录中找到的每个文件或目录执行闭包代码
  2. dir.eachFileRecurse { file ->
  3. println file.name
  4. }
  5. // 仅在文件上执行闭包代码,但递归地执行
  6. dir.eachFileRecurse(FileType.FILES) { file ->
  7. println file.name
  8. }

对于更复杂的遍历技术,您可以使用该traverse方法,该方法需要您设置一个特殊标志来指示如何处理遍历:

  1. dir.traverse { file ->
  2. if (file.directory && file.name=='bin') {
  3. // 如果当前文件是一个目录并且它的名字是bin,停止遍历
  4. FileVisitResult.TERMINATE
  5. } else {
  6. // 否则打印文件名并继续
  7. println file.name
  8. FileVisitResult.CONTINUE
  9. }
  10. }

4.数据和对象

在 Java 中,分别使用java.io.DataOutputStreamjava.io.DataInputStream类来序列化和反序列化数据并不少见。Groovy 将使处理它们变得更加容易。例如,您可以将数据序列化为文件并使用以下代码对其进行反序列化:

  1. boolean b = true
  2. String message = 'Hello from Groovy'
  3. // 序列化数据到文件中
  4. file.withDataOutputStream { out ->
  5. out.writeBoolean(b)
  6. out.writeUTF(message)
  7. }
  8. // ...
  9. // 然后再读取一编
  10. file.withDataInputStream { input ->
  11. assert input.readBoolean() == b
  12. assert input.readUTF() == message
  13. }

同样,如果您要序列化的数据实现了Serializable接口,您可以继续使用对象输出流,如下所示:

  1. Person p = new Person(name:'Bob', age:76)
  2. // 序列化数据到文件中
  3. file.withObjectOutputStream { out ->
  4. out.writeObject(p)
  5. }
  6. // ...
  7. // 然后再读取一编
  8. file.withObjectInputStream { input ->
  9. def p2 = input.readObject()
  10. assert p2.name == p.name
  11. assert p2.age == p.age
  12. }

5.执行外部进程

上一节描述了在 Groovy 中处理文件、阅读器或流是多么容易。然而,在系统管理或 devops 等领域,通常需要与外部进程进行通信。
Groovy 提供了一种执行命令行进程的简单方法。只需将命令行编写为字符串并调用该execute()方法。例如,在 nix 机器(或安装了适当 nix 命令的 Windows 机器)上,您可以执行以下命令:

  1. // 在外部进程中执行ls命令
  2. def process = "ls -l".execute()
  3. // 使用命令的输出并检索文本
  4. println "Found text ${process.text}"

execute()方法返回一个java.lang.Process实例,该实例随后将允许处理输入/输出/错误流以及检查进程的退出值等。
例如,这里是与上面相同的命令,但我们现在将一次处理一行结果流:

  1. // 在外部进程中执行ls命令
  2. def process = "ls -l".execute()
  3. // 对于进程的输入流的每一行
  4. process.in.eachLine { line ->
  5. // 打印
  6. println line
  7. }

值得注意的是,in对应于命令标准输出的输入流。out是指一个流,您可以在其中向流程发送数据(其标准输入)。

请记住,许多命令是 shell 内置的,需要特殊处理。因此,如果您想要 Windows 机器上目录中的文件列表并编写:

  1. def process = "dir".execute()
  2. println "${process.text}"

你会收到一个IOException:Cannot run program “dir”: CreateProcess error=2, The system cannot find the file specified。

这是因为dir它内置在 Windows shell ( cmd.exe) 中,不能作为简单的可执行文件运行。相反,您将需要编写:

  1. def process = "cmd /c dir".execute()
  2. println "${process.text}"

此外,由于此功能当前使用 java.lang.Process实现,因此必须考虑该类的缺陷。特别是,这个类的 javadoc :

由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能导致子进程阻塞,甚至死锁

正因为如此,Groovy 提供了一些额外的帮助方法,使流程的流处理更容易。
以下是如何从您的进程中吞噬所有输出(包括错误流输出):

  1. def p = "rm -f foo.tmp".execute([], tmpDir)
  2. p.consumeProcessOutput()
  3. p.waitFor()

consumeProcessOutput也有不同的版本,使用StringBuffer、InputStream、OutputStream等…请阅读 java.lang.Process 的 GDK API

此外,这些是一个pipeTo命令(映射到| 允许重载),它允许将一个进程的输出流馈送到另一个进程的输入流。
以下是一些使用示例:
管道在行动

  1. proc1 = 'ls'.execute()
  2. proc2 = 'tr -d o'.execute()
  3. proc3 = 'tr -d e'.execute()
  4. proc4 = 'tr -d i'.execute()
  5. proc1 | proc2 | proc3 | proc4
  6. proc4.waitFor()
  7. if (proc4.exitValue()) {
  8. println proc4.err.text
  9. } else {
  10. println proc4.text
  11. }

消费错误

  1. def sout = new StringBuilder()
  2. def serr = new StringBuilder()
  3. proc2 = 'tr -d o'.execute()
  4. proc3 = 'tr -d e'.execute()
  5. proc4 = 'tr -d i'.execute()
  6. proc4.consumeProcessOutput(sout, serr)
  7. proc2 | proc3 | proc4
  8. [proc2, proc3].each { it.consumeProcessErrorStream(serr) }
  9. proc2.withWriter { writer ->
  10. writer << 'testfile.groovy'
  11. }
  12. proc4.waitForOrKill(1000)
  13. println "Standard output: $sout"
  14. println "Standard error: $serr"