倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Groovy JDK? It's GDK! (File 編1) バイト配列・バイトストリーム

Groovy では GDK の機能によって java.io.File クラスにいろいろメソッドが追加されていて、ファイル入出力が非常に手軽になっています。 その反面、追加されているメソッド数が多いため、どんなメソッドがあるのか把握し切れていない人も多いのではないでしょうか。 拙者は正直いままで騙し騙し使っておりましたw ってことで、ここいらで GDK が File クラスに追加しているメソッドをおさらいしておこうかと。 べっ、別に Google I/O が話題になってるので、パッ見似ている Groovy I/O 関連の記事を書こう!、みたいな安直な考えなんかじゃないんだからねっ。 タイトルは id:fumokmm 氏のNo Programming, No Life 「GroovyなJDK、それがGDK」より*1

目次
1記事じゃ収まらなさそうなのでいくつかに分割。 予定ではこんな感じ:

  1. バイト配列・バイトストリーム
  2. 文字列・文字ストリーム
  3. 行処理
  4. ファイル・ディレクトリの走査
  5. データストリーム・オブジェクトストリーム
  6. その他のメソッド
  7. traverse() 再訪

今回はバイト配列・バイトストリーム関連のメソッド。

参考 URL

バイト配列

まずはバイト配列関連のメソッドを見ていきましょう。  この類のメソッドは下表の通り:

メソッド名 返り値 since
一括読み込み readBytes()
getBytes()
byte[] 1.0
1.7.1
反復読み込み eachByte(Closure c)
eachByte(int bufsSze, Closure c)
void 1.0
1.7.4
値の上書き setBytes(byte[] bytes) void 1.7.1
値の追加書き込み append(byte bytes)
leftShift(byte
bytes)
void
File
1.5.1
1.5.0

一応、メソッドの分類の説明をしておきます。 「一括読み込み」はファイルの全内容を1つのオブジェクトとして返します。 「反復読み込み*2」は Groovy で eachXxxx() という名前のメソッドを指していて、引数に Closure をとって反復的に読み込まれる値に対してその Closure の処理を実行します。 「値の上書き」は前のファイル内容を消して、引数に指定された内容を新たに書き込みます。 「値の追加書き込み」は前のファイル内容の末尾に、引数に指定された内容を追加します。 これらの分類は、後ほど byte 配列以外の場合にも使います。

で、byte 配列関連のメソッドに話を戻しましょう。 readByte() と getByte() は(実装は知りませんが)返されるバイト配列は同じようです。 JavaBeans っぽいメソッド getBytes() が後で追加されたのかな? append() と leftShift() (<< 演算子)もだいたい同じようですが、こちらは返り値が異なります。 leftShift() の返り値は File オブジェクトになっているので

file << firstBytes << secondBytes

のように続けて追加書き込みができます。

ではサンプルコード。 

// *****準備*****
// 内容が「Groovy」のファイルを作製
def src = new File('source.txt')
src.text = 'Groovy'

// 一括読み込み getBytes()
assert src.bytes == 'Groovy'.bytes

// 反復読み込み eachByte(Closure)
def sb = '' << ''    // StringBuffer
src.eachByte{ byte b -> sb << b.toString() }
assert sb.toString() == '71114111111118121'

// 値の上書き setBytes(byte[])
src.bytes = 'Grooovy'.bytes
assert src.text == 'Grooovy'

// 値の追加書き込み append(byte[]), leftShift(byte[]), <<
src.append('JDK')
//src << 'JDK'
assert src.text == 'GrooovyJDK'

使い方は簡単だと思います。 ちなみに src.bytes は src.getBytes() と、src.bytes = ... は src.setBytes(...) と同じです。

バイトストリーム

次はバイトストリーム、すなわち InputStream / OutputStream 関連のメソッドを見ていきしょう。 

メソッド名 返り値 since
入出力オブジェクトの取得 newInputStream()
newOutputStream()
BufferedInputStream
BufferedOutputStream
1.0
読み込み withInputStream(Closure closure) Object 1.5.2
書き込み withOutputStream(Closure closure)
append(InputStream stream)
leftShift(InputStream data)
Object
void
File
1.5.2
1.5.0
1.5.0

入出力オブジェクトの取得メソッド newInputStream(), newOutputStream() はバッファ付き (buffered) のバイトストリーム*3 BufferedInputStream / BufferedOutputStream が返されます。 append(), leftShift() はバイト配列の読み込みと同じです(返り値に関しても)。

ここで、というか GDK が File クラスに追加するメソッドで重要なのは withXxxx() という名前のメソッドです。  これらのメソッドは通常 Closure オブジェクトを引数にとり、その Closure に Xxxx にあたるオブジェクトを渡して処理を実行させるのですが、その処理の前後にストリームの開閉を自動で行ってくれます。 まぁ、Java7 で導入された try-with-resources 文のようなことをもっと気軽に(?)行ってくれると思えばいいでしょうか。

ではサンプルコード。 withInputStream() / withOutputStream() だけで充分でしょう:

// *****準備*****
// 内容が「Groovy」のファイルを作製
def src = new File('source.txt')
src.text = 'Groovy'

// 読み込み withInputStream()
src.withInputStream{ BufferedInputStream input ->  // 型を明示する必要はないっす。
    def bytes = new byte[3]
    input.read(bytes)
    assert bytes == 'Gro'.bytes
}

// 書き込み withOutputStream()
src.withOutputStream{ BufferedOutputStream output ->
    output.write('GDK'.bytes)
}
assert src.text == 'GDK'

// 各バイトを列挙したコピー(普通はこんなことをしないけどw)
def dest = new File('dest.txt')
src.withInputStream{ input ->
    dest.withOutputStream{ output ->
        for(i in input) output.write(i)
        // JavaScript の foreach みたいなの。
        // 「in」は Groovy ではキーワード。
    }
}
assert dest.text == 'GDK'

withInputStream(), withOutputStream() で Closure に渡されるオブジェクトはどちらもバッファ付きのバイトストリームです。 たぶん newInputStream() / newOutputStream() で生成されてるんじゃないかなぁ~。 サンプルコードの最後で、各バイトを列挙してファイル内容をコピーするサンプルを書いてみました。 Java で書くとこんなもんじゃおさまらんでしょう。

とりあえず、今回はバイト配列、バイトストリームに関するメソッドを見てきました。 次回はキャラクターセット(エンコーディング)情報が付加された文字列・文字ストリームについて、同様のメソッドを見ていく予定。

プログラミングGROOVY

プログラミングGROOVY

  • 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
  • 出版社/メーカー: 技術評論社
  • 発売日: 2011/07/06
  • メディア: 単行本(ソフトカバー)
  • 購入: 6人 クリック: 392回
  • この商品を含むブログ (155件) を見る
Groovyイン・アクション

Groovyイン・アクション

  • 作者: Dierk Konig,Andrew Glover,Paul King,Guillaume Laforge,Jon Skeet,杉浦孝,櫻井正樹,須江信洋,関谷和愛,佐野徹郎,寺沢尚史
  • 出版社/メーカー: 毎日コミュニケーションズ
  • 発売日: 2008/09/27
  • メディア: 単行本(ソフトカバー)
  • 購入: 5人 クリック: 146回
  • この商品を含むブログ (121件) を見る

*1:パッ、パクリなんかじゃないんだからねっ

*2:公式の用語ではありません。 勝手につけました。

*3:read(), write() メソッドが呼び出されるたびにファイル入出力を行うのではなく、メモリ上のバッファに値をストックしておいて入出力の効率を上げる実装、みたいな説明で OK?