Groovy学习之-Groovy Development Kit(GDK)-IO操作

Groovy学习目录-传送门

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"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,764评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,235评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,965评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,984评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,984评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,471评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,844评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,818评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,359评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,385评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,515评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,114评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,836评论 3 338
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,291评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,422评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,064评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,581评论 2 365

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,745评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,874评论 6 342
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,682评论 18 399
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,430评论 25 707
  • ViewPager 说起ViewPager绝对是应用最广的控件了,几乎每个应用都有它的身影。像是引导页面或广告等,...
    Il_mondo阅读 215评论 0 0