倭マン's BLOG

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

Groovy JDK? It's GDK! (File 編2) 文字列・文字ストリーム

前回に続き、今回は GDK が java.io.File オブジェクトに追加する文字列・文字ストリーム関連のメソッドを見ていきます(目次)。 前回のバイト配列・バイトストリーム関連のメソッドよりよく使うかと思います。

文字はデータ的に見れば「バイト + 文字セット(エンコーディング)」なので、前回のバイト配列・バイトストリーム関連のメソッドと比べて文字セット(を表す文字列)をとるメソッド引数が追加で定義されています*1。 エンコーディングを指定していない場合は(たぶん)各 OS のデフォルト・エンコーディングが使われます。 文字セットの指定に java.nio.charset.Charset オブジェクトは(直接は)使えません。

文字列

まずは文字列、すなわち String オブジェクトを使ってファイル内容を読み書きするメソッドを見ていきましょう。 分類は前回のバイト配列のときと同じにしています:

メソッド 返り値 since
一括読み込み getText()
getText(String cs)
String 1.0
反復読み込み 次回の行処理参照
値の上書き write(String text)
write(String text, String cs)
setText(String text)
setText(String text, String cs)
void 1.0
1.0
1.5.1
1.7.3
値の追加書き込み append(Object text)
append(Object text, String cs)
leftShift(Object text)
void
void
File
1.0

文字セットを指定する引数の色を変えているのは、単なる見やすさのためです。 バイト配列の場合の反復読み込みに対応する、文字を1文字ずつ読み込んで処理するメソッドはありません。 代わりに BufferedReader のように1行ずつ読み込んで処理するメソッドがあります。 ただし、ここでは長くなるので次回「行処理」に持ち越し。

その他の注意点は

  • setText() メソッドは write() メソッドと同じ
  • leftShift() はおそらく append() の処理を行って File オブジェクトを返すのと同じです(実装は知りませんが)。 ただし leftShift() は通常 << 演算子として使うので、文字セットを指定するメソッドは定義されていません
  • append(), leftShift() は内容を指定する引数の型が String 型ではなく Object 型となっています。 内容となる文字列は、多くのオブジェクトに対しては単にそのオブジェクトの toString() を呼び出した返り値になりますが、Reader オブジェクトやあとで見る groovy.lang.Writable オブジェクトの場合は適切な内容を書き出してくれます(ちなみに ObjectOutputStream とかは関係ありません)。

ではサンプルコード。

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

def srcUTF8 = new File('src-utf8.txt')
srcUTF8.setText('倭マン', 'UTF-8')

// 一括読み込み getText()
assert src.text == 'Groovy'
assert srcUTF8.getText('UTF-8') == '倭マン'

// 値の上書き setText()
src.text = 'Grooovy'
assert src.text == 'Grooovy'

srcUTF8.setText('和マン', 'UTF-8')
assert srcUTF8.getText('UTF-8') == '和マン'

// 値の追加書き込み append(), leftShift(), <<
src.text = 'Gr'
src.append('oo')
src << 'v' << 'y'
assert src.text == 'Groovy'

srcUTF8.setText('倭', 'UTF-8')
srcUTF8.append('マン', 'UTF-8')
assert srcUTF8.getText('UTF-8') == '倭マン'

まぁ、バイト配列の場合と同じ、というかもっと分かりやすいかと思います。 文字セットを指定しない getText() / setText() は text プロパティへのアクセスのように書けるので便利ですが、日本語などを使う場合は文字セットを指定しておく方が無難でしょう。

文字ストリーム

次は文字ストリーム、つまり Reader / Writer 関連のメソッド。

メソッド 返り値 since
入出力オブジェクト
の取得
newReader()
newReader(String cs)
newWriter()
newWriter(boolean append)
newWriter(String cs)
newWriter(String cs, boolean append)
newPrintWriter()
newPrintWriter(String cs)
BufferedReader
BufferedReader
BufferedWriter
BufferedWriter
BufferedWriter
BufferedWriter
PrintWriter
PrintWriter
1.0
読み込み withReader(Closure c)
withReader(String cs, Closure c)
Object 1.5.2
1.6.0
書き出し withWriter(Closure c)
withWriter(String cs, Closure c)
withWriterAppend(Closure c)
withWriterAppend(String cs, Closure c)
Object 1.5.2
プリント出力 withPrintWriter(Closure c)
withPrintWriter(String cs, Closure c)
Object 1.5.2
  • withWriter() と withWriterAppend() の違いは「上書き」か「追加書き込み」かです。
  • プリント出力 withPrintWriter() は書き出し withWriter と同じようなものですが、引数の Closure に渡されるのが (Buffered)Writer ではなく PrintWriter であることが違います。 Writer と PrintWriter の違いは・・・ JavaDoc とか見てください*2

withXxxx() メソッドで文字セットを指定する場合、

src.withReader('UTF-8', {
    println it.readLine()
})

のようにしても動作しますが、通常は

src.withReader('UTF-8'){
    println it.readLine()
}

のようにした方が見易いです。 以上を踏まえてサンプルコード:

// *****準備*****
// 文章は Groovy の HP (http://groovy.codehaus.org/) より
def src = new File('source.txt')
src.text = '''\
Groovy is an agile and dynamic language for the Java Virtual Machine
Groovy builds upon the strengths of Java but has additional power features inspired by languages like Python, Ruby and Smalltalk'''

def srcUTF8 = new File('utf8.txt')
srcUTF8.setText('''\
Groovy makes modern programming features available to Java developers with almost-zero learning curve
Groovy provides the ability to statically type check and statically compile your code for robustness and performance''', 'UTF-8')


// withReader()
src.withReader{ reader ->
    assert reader instanceof LineNumberReader
    assert reader.readLine() ==~ /Groovy is an agile and dynamic.*/    // 「Groovy is ...」 で始まる
    assert reader.readLine() ==~ /Groovy builds upon the strengt.*/
}

srcUTF8.withReader('UTF-8'){ reader ->
    assert reader instanceof BufferedReader
    assert reader.readLine() ==~ /Groovy makes modern programmin.*/
    assert reader.readLine() ==~ /Groovy provides the ability to.*/
}


// withWriter()
src.withWriter{ writer ->
    assert writer instanceof BufferedWriter
    writer.write 'Groovy supports Domain-Specific Languages and other compact syntax so your code becomes easy to read and maintain'
}
assert src.text ==~ /Groovy supports Domain-Specifi.*/

srcUTF8.withWriter('UTF-8'){ writer ->
    assert writer instanceof BufferedWriter
    writer.write 'Groovy makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL'
}
assert srcUTF8.getText('UTF-8') ==~ /Groovy makes writing shell and.*/


// withWriterAppend()
src.withWriterAppend{ writer ->
    assert writer instanceof BufferedWriter
    writer.write 'Groovy increases developer productivity by reducing scaffolding code when developing web, GUI, database or console applications'
}
assert src.text ==~ /Groovy supports Doma.*console applications/

srcUTF8.withWriterAppend('UTF-8'){ writer ->
    assert writer instanceof BufferedWriter
    writer.write 'Groovy makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL'
}
assert srcUTF8.getText('UTF-8') ==~ /Groovy makes writing.*ities and an Ant DSL/


// withPrintWriter()
src.withPrintWriter{ writer ->
    assert writer instanceof PrintWriter
    writer.printf '%s simplifies testing by supporting unit testing and mocking out-of-the-box', 'Groovy'
}
assert src.text ==~ /Groovy simplifies testing by s.*/

srcUTF8.withPrintWriter('UTF-8'){ writer ->
    assert writer instanceof PrintWriter
    writer.printf '%s seamlessly integrates with all existing Java classes and libraries', 'Groovy'
}
assert srcUTF8.getText('UTF-8') ==~ /Groovy seamlessly integrates w.*/


// ファイル内容のコピー
def src = new File('source.txt')
src.text = '''\
Groovy...
is an agile and dynamic language for the Java Virtual Machine
builds upon the strengths of Java but has additional power features inspired by languages like Python, Ruby and Smalltalk
makes modern programming features available to Java developers with almost-zero learning curve
provides the ability to statically type check and statically compile your code for robustness and performance
supports Domain-Specific Languages and other compact syntax so your code becomes easy to read and maintain
makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL
increases developer productivity by reducing scaffolding code when developing web, GUI, database or console applications
simplifies testing by supporting unit testing and mocking out-of-the-box
seamlessly integrates with all existing Java classes and libraries
compiles straight to Java bytecode so you can use it anywhere you can use Java'''

def dest = new File('dest.txt')

src.withReader{ reader ->
    dest.withPrintWriter{ writer ->
        for(line in reader) writer.println(line)
    }
}

ちょっと分かりづらいサンプルだったかな。

オブジェクト変換とWritable インターフェース

GDK は File クラスに対して groovy.lang.Writable オブジェクトに変換するメソッドを追加しています。 Writable クラスは writeTo() メソッドを持ち、自身の内容に対応する適切な内容を引数の Writer オブジェクトに書き出します:

package groovy.lang

public interface Writable{
    java.io.Writer writeTo(java.io.Writer out)
}

File クラスに追加されている、オブジェクト変換に関するメソッドは以下の通り:

メソッド 返り値 since
asWritable()
asWritable(String encoding)
File 1.0
asType(Class c) Object 1.0

asType(Class) は引数の Class オブジェクトが Writable.class なら asWritable() で返される Writable オブジェクトを返します*3

あまり明示的に使うものなのかどうか知りませんが、使う場合は << 演算子(もしくは append(), leftShift())と一緒に使うのがいいでしょう:

def src = new File('source.txt')
src.text = '...'

def dest = new File('dest.txt')

dest << src.asWritable()    // 「source.txt」の内容が書き出される
//dest << (src as Writable)    // 上に同じ
//dest << src    // 「source.txt」が書き込まれるよ!

次回は文字列・文字ストリーム関連で今回扱わなかった行処理に関連するメソッドを見ていく予定。

プログラミング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:使う分には print(), println(), printf(), format() などのメソッドが使える、というのが大きな違いでしょうか。 他には自動フラッシュのタイミングや例外が投げられないなどの違いがあるようで。

*3:file.asType(Writable) は as キーワードを使って「simeFile as Writable」のように書いても呼び出されます。