No Programming, No Life

プログラミング関連の話題や雑記

GroovyなJDK、それがGDK(String編その2)


このシリーズの一覧はこちら

はじめに

GroovyにはJavaの標準API(JDK)を拡張したGroovy JDK(GDK)があります。大量の便利メソッドが追加されており、これを使いこなすだけでも相当のことができるようになります。このシリーズでは毎回1クラスずつ各メソッドの使用例をサンプル付きでご紹介していきたいと思います。
「今回は java.lang.String です」


String編その2では区切り系、行区切り系、改行コード系、プロセス実行系のメソッドをご紹介します。それでは早速見て行きましょう。

改行コード系メソッド

String[] split() [since v1.5.0]

ホワイトスペース(" \t\n\r\f")で区切った文字列配列を返却してくれるコンビニエンスメソッドです。
これはわりと便利で、'aaa bbb ccc' のようにスペース区切りやタブ区切りの文字列を簡単にリスト化できます。

assert ['aaa', 'bbb', 'ccc'] == 'aaa bbb     ccc'.split()
assert ['aaa', 'bbb', 'ccc'] == 'aaa\tbbb\tccc'.split()

参照: List tokenize()

Object splitEachLine(String regex, Closure closure) [since v1.5.5]

文字列を改行で区切ってその行を指定された区切り文字(正規表現)でsplit()してからクロージャに渡します。

'''aaa->bbb
ccc->ddd
eee->fff'''.splitEachLine('->') {
  println "key:${it[0]}, value:${it[1]}"
}
出力結果
key:aaa, value:bbb
key:ccc, value:ddd
key:eee, value:fff

このメソッド自体の戻り値は最後に評価されたクロージャの値となるようです。
なので、この場合、printlnはnullを返すので、式自体の結果はnullとなります。

Object splitEachLine(Pattern pattern, Closure closure) [since v1.6.8]

Object splitEachLine(String regex, Closure closure)のPattern版。

List tokenize() [since v1.0]

ホワイトスペース(" \t\n\r\f")で区切った文字列リストを返却してくれるコンビニエンスメソッドです。
これはわりと便利で、'aaa bbb ccc' のようにスペース区切りやタブ区切りの文字列を簡単にリスト化できます。

assert ['aaa', 'bbb', 'ccc'] == 'aaa bbb     ccc'.tokenize()
assert ['aaa', 'bbb', 'ccc'] == 'aaa\tbbb\tccc'.tokenize()

内部的にはStringTokenizerを使っているようです。

参照: String[] split()

List tokenize(String token) [since v1.0]

Stringをセパレータとして、この文字列を分割した結果のリストを返却します。

assert 'a::b::c'.tokenize('::') == ['a', 'b', 'c']

内部的にはStringTokenizerを使っているようです。

List tokenize(Character token) [since v1.7.2]

charをセパレータとして、この文字列を分割した結果のリストを返却します。

char pathSep = ':'
assert "/tmp:/usr".tokenize(pathSep) == ["/tmp", "/usr"]

内部的にはStringTokenizerを使っているようです。

List toList() [since v1.0]

文字列を1文字ずつ分割したStringのリストにして返却します。

assert 'abcde'.toList() == ['a', 'b', 'c', 'd', 'e']

このメソッドを利用して一度文字列をリストにしておけば、
リストに対してのシーケンシャルな操作が可能となるため便利です。

List readLines() [since v1.5.5]

文字列を各行ごとに分割してリストにします。

assert '''\
aaa
bbb
ccc'''.readLines() == ['aaa', 'bbb', 'ccc']

Object eachLine(Closure closure) [since v1.5.5]

クロージャを受け取って、各行ごとに処理をさせます。

def str = '''aaa
bbb
ccc
'''.eachLine {
  println it
}
出力
aaa
bbb
ccc

ちなみに、クロージャの第2パラメータを与えると、行番号(0から開始)が取得できます。

def str = '''aaa
bbb
ccc
'''.eachLine { it, no ->
  println "$no: $it"
}
出力
0: aaa
1: bbb
2: ccc

Object eachLine(int firstLine, Closure closure) [since v1.5.7]

クロージャを受け取って、各行ごとに処理をさせます。firstLine数値を指定するとその数値から行番号が始まります。*1

def str = '''aaa
bbb
ccc
'''.eachLine(10) { it, no ->
  println "$no: $it"
}
出力
10: aaa
11: bbb
12: ccc

こちらのfirstLineを引数で指定する版の場合、クロージャの方で第2パラメータを指定しないと無意味です。

String normalize() [since v1.6.0]

文字列中に含まれる改行コード(LF or CR/LF or CR)をLFに統一(normalize)します。

def cr = 'aaa\rbbb\rccc'
def lf = 'aaa\nbbb\nccc'
def crlf = 'aaa\r\nbbb\r\nccc'

def normalizedCr = cr.normalize()
def normalizedLf = lf.normalize()
def normalizedCrLf = crlf.normalize()

assert normalizedCr.count('\n') == 2 // 改行文字はLFになっている
assert normalizedCr.count('\r\n') == 0 // CRLFはない
assert normalizedCr.count('\r') == 0 // CRはない

assert normalizedLf.count('\n') == 2 // 改行文字はLFになっている
assert normalizedLf.count('\r\n') == 0 // CRLFはない
assert normalizedLf.count('\r') == 0 // CRはない

assert normalizedCrLf.count('\n') == 2 // 改行文字はLFになっている
assert normalizedCrLf.count('\r\n') == 0 // CRLFはない
assert normalizedCrLf.count('\r') == 0 // CRはない

String denormalize() [since v1.6.0]

文字列中に含まれる改行コード(LF or CR/LF or CR)をプラットフォームの改行コードに合わせて変換(denormalize)します。

def cr = 'aaa\rbbb\rccc'
def lf = 'aaa\nbbb\nccc'
def crlf = 'aaa\r\nbbb\r\nccc'

def denormalizedCr = cr.denormalize()
def denormalizedLf = lf.denormalize()
def denormalizedCrLf = crlf.denormalize()

// どんな改行コードの文字列でも
// プラットフォームに合わせた改行コードへと変換される
assert [
  denormalizedCr,
  denormalizedLf,
  denormalizedCrLf
].count(denormalizedCr) == 3 // 全て同じ文字列

Process execute() [since v1.0]

文字列をコマンドと見立ててコマンドを実行します。
戻り値はProcessなので以下サンプルではinputStreamから出力された文字列を取得しています。

for *nix
println 'ls -l'.execute().in.text

Process execute(String[] envp, File dir) [since v1.0]

環境変数とカレントディレクトリを指定してコマンドを実行します。

println 'env'.execute(['ABC=10'], new File('.')).in.text

Process execute(List envp, File dir) [since v1.0]

execute(String[] envp, File dir)参照。

*1:int型なのでマイナスでも大丈夫です。