《Groovy官方文檔》Groovy開發套件-使用IO
Groovy開發套件 第一部分
1 I/O 的使用
Groovy提供了豐富的方法來操作IO流。當然你也可以使用標準的Java代碼來進行這些操作。但是Groovy提供了更多方便的方式來操作文件,流…
你可以先看看下麵列舉的一些方法:
- the io.File class : https://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
- the io.InputStream class: https://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
- the io.OutputStream class: https://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
- the io.Reader class: https://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
- the io.Writer class: https://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
- the nio.file.Path class: https://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html
下麵的一些小節將提供一些示例來演示如何使用這些類,如果你想查看所有方法的詳細用法,請閱讀GDK的接口文檔。
1.1 讀文件
作為開篇的第一個示例,我們來看看如何使用Groovy來讀文件並且打印讀到的所有行:
new File(baseDir, 'haiku.txt').eachLine { line ->
println line
}
Groovy的eachLine方法是File類自動加載並且可有有許多的變體,比如如果你想知道行號,你可以使用以下的變體:
new File(baseDir, 'haiku.txt').eachLine { line, nb ->
println "Line $nb: $line"
}
在eachLine方法體裏,拋出任何異常後該方法都可以確保正常將文件流關閉。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')
}
}
}
如果你想將一個文本文件中的所有行放到一個list裏,你可以這樣寫:
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
使用IO並不僅僅是操作文件,事實上,更多的時候你需要操作輸入/輸出流,這就是為什麼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 ...
}
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
} //(1)
dir.eachFileMatch(~/.*\.txt/) { file ->
println file.name
} //(2)
- 列舉給定目錄下的每個文件
- 在給定目錄下查找匹配格式的文件
你經常需要處理更深層次的目錄,可以使用 eachFileRecurse:
dir.eachFileRecurse { file ->
println file.name
} //(1)
dir.eachFileRecurse(FileType.FILES) { file ->
println file.name
} //(2)
- 遞歸列舉所有文件和目錄
- 僅僅遞歸列舉文件
需要更加複雜的遍曆技術,可以使用 traverse方法,需要你設置一個特定的遞歸標識來終止遞歸:
dir.traverse { file ->
if (file.directory && file.name=='bin') {
FileVisitResult.TERMINATE //(1)
} else {
println file.name
FileVisitResult.CONTINUE //(2)
}
}
- 如果當前文件是一個目錄並且名字是bin,停止遍曆
- 打印文件名並繼續
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來處理文件,Readers或流是一件很簡單的是。但是在一些領域向係統管理員或開發經常需要和外部進程進行交互。
Groovy提供了一種簡單的方式來執行命令行進程。僅僅需要將命令行寫成字符串然後調用execute方法。舉個例子,子啊一個*nix機器上(或者一台安裝了*nix命令執行環境的windows機器上)你可以執行下麵的代碼:
def process = "ls -l".execute() (1)
println "Found text ${process.text}" (2)
- 在外部進程執行ls命令
- 處理輸出並且返回文本
Execute方法返回一個java.lang.Process實例,可以使用in/out/err流來處理,通過返回值可以查看處理情況。
eg:這裏有一個和上麵命令類似但是現在是每次處理一個結果流:
def process = "ls -l".execute() (1)
process.in.eachLine { line -> (2)
println line (3)
}
- 在外部進程執行ls命令
- 對每個輸入流進行處理
- 打印line的內容
in 相當於標準輸出命令中的輸入流, out指代你發送到進程(標準輸入流)中的數據的流。
記住,對於內置的shell命令需要有特殊的處理,因此如果你想在一台windows機器上列一個某個目錄的所有文件可以這樣寫:
def process = "dir".execute()
println "${process.text}"
當出現 Cannot run program “dir”: CreateProcess error=2, The system cannot find the file specified. 時你將收到一個IOException
這是因為dir命令是windows shell(cmd.exe)內置的命令。不能僅僅是執行dir,你應該這樣寫:
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()
consumeProcessOutput 也有一些變體來使用StringBuffer,InputStream,OutputStream等等,完整的例子可以參考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"
最後更新:2017-05-22 13:01:22