Groovy提供了一些辅助方法来帮助开发者开发I/O程序,开发过程中你可以使用标准的java代码,但Groovy提供了更多简便的方法来处理files、streams、readers,一起来看一下。
1、读取文件
def baseDir = "/Users/jiangxiaoma/Documents"
new File(baseDir, 'test.txt').eachLine { line ->
println line
}
eachLine
方法是被Groovy自动添加到File
类的方法并有不同版本,比如要加上行号,你可以这样:
new File(baseDir, 'test.txt').eachLine { line, nb ->
println "Line $nb: $line"
}
在eachLine
闭包内发生任何异常,该方法都能确保源文件被正常关闭。
在某些情况下你可能使用Reader
,你仍将从Groovy的自动资源管理中获益,下面的例子,如果发生异常,reader将自动被关闭:
def count = 0, MAXSIZE = 3
new File(baseDir,"test.txt").withReader { reader ->
while (reader.readLine()) {
if (++count > MAXSIZE) {
throw new RuntimeException('Test.txt should only have 3 verses')
}
}
}
如果你想把文件内容各行放进一个list,你可以这样:
def list = new File(baseDir, 'test.txt').collect {it}
或者你可以使用as
将文件内容放入一个array:
def array = new File(baseDir, 'test.txt') as String[]
如果想把文件内容直接放进一个byte[]
,可以这样:
def file = new File(baseDir, 'test.txt')
byte[] contents = file.bytes
在进行I/O操作的时候不仅限于文件,事实上,很多操作都依赖于输入输出流,下面例子是从File
中获取InputStream
:
def is = new File(baseDir,'test.txt').newInputStream()
// do something ...
is.close()
这种方式需要自己手动关闭流,使用withInputStream
方法将会自动关闭流:
new File(baseDir,'haiku.txt').withInputStream { stream ->
// do something ...
}
2、写入文件
在某些情况下,你可能不仅需要读取文件,也要写入文件,写入文件使用Writer
:
new File(baseDir,'test.txt').withWriter('utf-8') { writer ->
writer.writeLine 'Into the ancient pond'
writer.writeLine 'A frog jumps'
writer.writeLine 'Water’s sound!'
}
更简单的方法是使用<<
操作符:
new File(baseDir,'test.txt') << '''Into the ancient pond
A frog jumps
Water’s sound!'''
如果你的test.txt里有内容,上面两种方式将会覆盖test.txt的内容。
当然你也可以直接用输出流来写入文件:
def os = new File(baseDir,'data.bin').newOutputStream()
// do something ...
os.close()
同样的,withOutputStream
方法将自动关闭输出流和处理异常:
new File(baseDir,'data.bin').withOutputStream { stream ->
// do something ...
}
3、遍历文件树
遍历文件夹是一个经常用到的功能,Groovy提供了一些方法来遍历,比如列出根目录下的所有文件和文件夹、找到符合正则表达式的标题的文件:
def dir = new File("/")
//eachFile()方法返回该目录下的所有文件和子目录,不递归
dir.eachFile { file ->
println file.name
}
dir.eachFileMatch(~/.*\.txt/) {file ->
println file.name
}
可能你需要处理更深目录层次的文件,或者只显示文件或者文件夹,你可以使用eachFileResource
:
def dir = new File("/")
//dir.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
}
}
4、数据和类的序列化&反序列化
在java中使用java.io.DataOutputStream
和java.io.DataInputStream
进行序列化和反序列化是非常常用的,使用Groovy将使之变得更容易,下面是序列化数据到文件和从文件读取数据进行反序列化:
boolean b = true
String message = 'Hello from Groovy'
def file = new File(baseDir, 'test.txt')
// 序列化数据到文件
file.withDataOutputStream { out ->
out.writeBoolean(b)
out.writeUTF(message)
}
// ...
// 从文件读取数据并反序列化
file.withDataInputStream { input ->
assert input.readBoolean() == b
assert input.readUTF() == message
}
同样的,如果一个类实现了Serializable
接口,可以将对象序列化到文件:
def file = new File(baseDir, 'test.txt')
Person p = new Person(name:'Bob', age:76)
// 序列化对象到文件
file.withObjectOutputStream { out ->
out.writeObject(p)
}
// ...
// 从文件读取数据进行反序列化
file.withObjectInputStream { input ->
def p2 = input.readObject()
assert p2.name == p.name
assert p2.age == p.age
}
5、程序中执行shell命令
Groovy提供了简单的方法执行shell命令:
def process = "ls -l".execute()
println "Found text ${process.text}"
//逐行处理
def process = "ls -l".execute()
process.in.eachLine { line ->
println line
}
execute()
方法返回了一个java.lang.Process
实例。
如果想执行windows下的dir
命令:
def process = "dir".execute()
println "${process.text}"
将返回IOExcepton
异常:“Cannot run program "dir": CreateProcess error=2, The system cannot find the file specified.”
这是因为dir
命令是windows下的shell命令(cmd.ext
),不能执行,应该这样写:
def process = "cmd /c dir".execute()
println "${process.text}"