IO操作

Groovy为I/O操作提供了许多帮助方法,虽然你可以在Groovy中用标准Java代码来实现I/O操作,不过Groovy提供了大量的方便的方式来操作File、Stream、Reader等等。

读取文件

读取文本文件并打印每一行文本

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

eachLine方法是Groovy为File类自动添加的方法,同时提供多个变体方法,比如你想知道读取的行数,你可以使用它的变体方法,如下

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

如果出于某种原因eachLine抛出异常,该方法能够确保资源正确地关闭,这适用于所有Groovy添加的I/O资源方法

例如在某些情况下,你更喜欢使用Reader访问I/O资源,但你仍然受益于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操作符来讲文件内容转为String数组

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

多少次你不得不获得一个文件的内容到一个byte[],它需要多少代码?Groovy使得这件事非常的容易。

byte[] contents = file.bytes

处理I/O并不局限于处理文件。事实上,很多的操作依赖于输入/输出流,这就是为什么Groovy添加大量的支持方法,正如你所看到的文档。

例如,你可以很容易从一个文件获得一个InputStream

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

但是你可以看到,这种方式需要你手动关闭流。会为你留意到,在Groovy中使用withInputStream通常是更好的方式。

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

写文件

当然,一些时候你并不想读取文件,而是要写文件。其中一个方式就是使用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()

但是你可以看到它需要你手动关闭输出流。会为你留意到,在Groovy中使用withOutputStream通常是更好的方式。

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

遍历文件树

在脚本中,在文件树种查找一些特定文件并处理这些文件是很常见的事情。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 ->
if (file.directory && file.name=='bin') {
//如果当前file是一个目录或者它的名字是 bin ,则停止遍历
FileVisitResult.TERMINATE
} else {
//打印文件名并继续遍历
println file.name
FileVisitResult.CONTINUE
} }

数据和对象

在Java中使用java.io.DataOutputStreamjava.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接口,你可以将它作为一个Object输出流处理,例如:

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
}

执行外部程序

前一节中描述的是使用Groovy处理文件多么容易。然而在系统管理等领域或devops通常需要与外部processes沟通。Groovy提供了一个简单的方法来执行命令行processes。仅仅需要些命令字符串再调用execute()方法即可。如。在 *nix机(安装了合适的 *nix命令工具的windows机),你这样执行:

//在外部process执行`ls`命令
def process = "ls -l".execute()
//读取文本并打印
println "Found text ${process.text}"

execute()方法返回一个java.lang.Process对象,并允许处理它的in/out/err流和退出值,从Process检查等。

如,这是和上面一样的命令

//执行`ls`命令
def process = "ls -l".execute()
//遍历命令进程的输入流
process.in.eachLine { line ->
//打印每一行输入流
println line
}

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

记住许多命令是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提供了一些额外的帮助方法,使得流处理更容易。

这里是如何从你的Process中gobble(未翻译)所有的输出(包括错误流输出):

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

还有consumeProcessOutput的变体,使用StringBufferInputStreamOutputStream等...有关完整的列表,请阅读GDK API for java.lang.Process

此外,这些是一个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"

Groovy系列(5)- Groovy IO操作的更多相关文章

  1. Java基础复习笔记系列 七 IO操作

    Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...

  2. Groovy系列-groovy比起Java--有哪些地方写起来更舒服?

    groovy比起java-有哪些地方写起来更舒服 java发展缓慢,语法落后冗余 说起java,其实java挺好的,java现在的性能也不错,但是,java的语法显然比较落后,而且冗余,getter/ ...

  3. Gradle系列之一 Groovy语法精讲

    Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语 ...

  4. Pandas系列(十一)-文件IO操作

    数据分析过程中经常需要进行读写操作,Pandas实现了很多 IO 操作的API,这里简单做了一个列举. 格式类型 数据描述 Reader Writer text CSV read_ csv to_cs ...

  5. openssl之BIO系列之6---BIO的IO操作函数

    BIO的IO操作函数     ---依据openssl doc/crypto/bio/bio_read.pod翻译和自己的理解写成          (作者:DragonKing Mail:wzhah ...

  6. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  7. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  8. Java IO系列之一:IO

    1. 概述 Java IO一般包含两个部分: 1.java.io包中堵塞型IO: 2.java.nio包中的非堵塞型IO,通常称为New IO. java.io包下,分为四大块近80个类: 1.基于字 ...

  9. 重叠io操作

    第一章 一. 重叠模型的优点 1. 可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统. 2. 比起阻塞.select.WSAAsyncSelect以及WSAEv ...

随机推荐

  1. 如何发送一个http请求—apipost

    API界面功能布局 API请求参数 Header 参数 你可以设置或者导入 Header 参数,cookie也在Header进行设置 Query 参数 Query 支持构造URL参数,同时支持 RES ...

  2. CTF--[BJDCTF2020]Cookie is so stable 1(SSTI)

    从hint.php可以找到提示,要求观察cookies 打开flag.php可以看到需要输入用户名,多次试验后发现输入的用户名会以cookies的方式储存 使用dirsearch扫描没有发现什么有用的 ...

  3. Oracle插入中文乱码问题

    PLSQL执行一条插入代码,两个字符既显示超长,一个字符插入后乱码 insert into person (pid, pname) values (1,'明'); Google查询说原因是Oracle ...

  4. CompletionService简介、原理以及小案例

    博客1:http://www.oschina.net/question/12_11255 博客2: CompletionService简介 CompletionService与ExecutorServ ...

  5. Linux centos 安装 Node.js

    官网下载地址 https://nodejs.org/zh-cn/download/ 1.下载二进制文件 (x64)   相当于  https://nodejs.org/dist/v10.16.3/no ...

  6. Mysql的undo、redo、binlog的区别

      与不同引擎的关系 核心作用 生命周期   日志类型 undo log 属于innodb引擎独有 回滚,保证事务的"原子性",事务日志  事务开始前,以类似"快照&qu ...

  7. 前端 | 页面触底自动加载 Vue 组件

    不管是 web 端还是移动端,信息流都是现在很流行的信息展示方式.信息流经常搭配自动加载一起使用以获得更好的使用体验. 最近在使用 Vue 开发过程中也遇到了首页信息流自动加载的需求.大致了解了一下几 ...

  8. java 接口代理

    接口 public interface Cc { void say(); } 实现类: public class C implements Cc{ @Override public void say( ...

  9. MySQL列举常见的关系型数据库和非关系型都有那些?

    关系型数据库: Oracle.DB2.Microsoft SQL Server.Microsoft Access.MySQL 非关系型数据库: NoSql.Cloudant.MongoDb.redis ...

  10. 操作系统的IO模型

    IO操作根据设备类型一般分为内存IO,网络IO,和磁盘IO.其中内存IO的速度大大快于后两者,计算机的性能瓶颈一般不在于内存IO. 尽管网络IO可通过购买独享带宽和高速网卡来提升速度,可以使用RAID ...