No Programming, No Life

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

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


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

はじめに

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

Stringは、おそらく一番良く使うであろう「文字列」を表現するクラスです。Javaでは文字列はダブルクォートで囲んだ形式を使いますが、Groovyではシングルクォートで囲んだものが通常のStringとなります*1。Groovyではダブルクォートで囲んだ場合、GStringというStringの機能を拡張したクラスとなります。これは文字列に変数を埋め込んだり、遅延評価させたりできます。また、正規表現のために /str/ という書き方がサポートされており、バックスラッシュ「\」をエスケープしなくてよいのと、GStringと同様に変数を埋め込むことが可能となっております*2

なお、String編は多数のメソッドが存在する関係で、5回に分けてご紹介したいと思います。

String編その1では文字加工系のメソッドをご紹介します。それでは早速見て行きましょう。

String capitalize() [since v1.7.3]

先頭の文字を大文字にする。

assert 'h'.capitalize() == 'H'
assert 'hello my friend.'.capitalize() == 'Hello my friend.'
assert 'hello my friend.'.tokenize(/ +/)*.capitalize().join(' ') == 'Hello My Friend.'
assert 'あいうえお'.capitalize() == 'あいうえお' // 全角文字は変わらない

String reverse() [since v1.0]

文字列をひっくり返した文字列を返却します。

assert 'cba' == 'abc'.reverse()

String padLeft(Number numberOfChars) [since v1.0]

文字列が指定文字数になるまで、文字列の左側を半角スペースで埋めていきます。

assert 'abc'.padLeft(5) == '  abc'
assert 'abc'.padLeft(2) == 'abc' // 文字列より少ない場合はそのまま
assert 'abc'.padLeft(-1) == 'abc'
assert 'あいう'.padLeft(5) == '  あいう' // 全角でもOK

String padLeft(Number numberOfChars, String padding) [since v1.0]

文字列が指定文字数になるまで、文字列の左側を指定の文字列で埋めていきます。

assert 'abc'.padLeft(5, '*') == '**abc'
assert 'abc'.padLeft(2, '*') == 'abc' // 文字列より少ない場合はそのまま
assert 'abc'.padLeft(-1) == 'abc'
assert 'あいう'.padLeft(5, '_') == '__あいう' // 全角でもOK
assert 'abc'.padLeft(10, '123') == '1231231abc' // 指定文字数内でパターンを繰り返す

String padRight(Number numberOfChars) [since v1.0]

文字列が指定文字数になるまで、文字列の右側を半角スペースで埋めていきます。

assert 'abc'.padRight(5) == 'abc  '
assert 'abc'.padRight(2) == 'abc' // 文字列より少ない場合はそのまま
assert 'abc'.padRight(-1) == 'abc'
assert 'あいう'.padRight(5) == 'あいう  ' // 全角でもOK

String padRight(Number numberOfChars, String padding) [since v1.0]

文字列が指定文字数になるまで、文字列の右側を指定の文字列で埋めていきます。

assert 'abc'.padRight(5, '*') == 'abc**'
assert 'abc'.padRight(2, '*') == 'abc' // 文字列より少ない場合はそのまま
assert 'abc'.padRight(-1) == 'abc'
assert 'あいう'.padRight(5, '_') == 'あいう__' // 全角でもOK
assert 'abc'.padRight(10, '123') == 'abc1231231' // 指定文字数内でパターンを繰り返す

String center(Number numberOfChars) [since v1.0]

文字を半角スペースで中央揃えしてくれます。左と右のパディングはいくつだっけ?*3とか面倒な計算はGroovyに任せましょう。

assert 'abc'.center(9)  == '   abc   '
assert 'abc'.center(10) == '   abc    ' // 字余りの場合は右側が多くなる
assert 'abc'.center(2) == 'abc' // 指定文字以下の場合は変わらない

String center(Number numberOfChars, String padding) [since v1.0]

上記のString#centerの半角スペースじゃなくて自由な文字列にしたバージョン。

assert 'abc'.center(9, '*')  == '***abc***'
assert 'abc'.center(10, '*') == '***abc****' // 字余りの場合は右側が多くなる
assert 'abc'.center(2, '_') == 'abc' // 指定文字以下の場合は変わらない
'abc'.center(10, 'あいう') == 'あいうabcあいうあ' // 全角文字もOK。指定文字数内でパターンを繰り返す

String expand() [since v1.7.3]

タブ文字(\t)を半角スペース8文字で置き換えます。

def expect8 = '''\
aaa
        bbb
ccc'''

// bbbの前のタブ文字(\t)を半角スペースへ
assert expect8 == '''\
aaa
\tbbb
ccc'''.expand()

// タブ文字(\t)の前に既に半角スペースがあっても
// ちゃんと8文字になるように計算してくれる
assert expect8 == '''\
aaa
  \tbbb
ccc'''.expand()

String expand(int tabStop) [since v1.7.3]

#expand()の半角スペース数を指定できるバージョン。

def expect4 = '''\
aaa
    bbb
ccc'''

// bbbの前のタブ文字(\t)を半角スペースへ
assert expect4 == '''\
aaa
\tbbb
ccc'''.expand(4)

// タブ文字(\t)の前に既に半角スペースがあっても
// ちゃんと4文字になるように計算してくれる
assert expect4 == '''\
aaa
  \tbbb
ccc'''.expand(4)

String expandLine(int tabStop) [since v1.7.3]

行を無視してタブ文字(\t)を指定した数分の半角スペースで置き換えます。

def expect4 = 'aaa \n   bbb\nccc' 

assert expect4 == '''\
aaa\t
\tbbb
ccc'''.expandLine(4)

※このメソッドはexpand()expand(int tabStop)から内部的に呼ばれているようでして
意味的にも改行を無視することになるので分かりにくくなるため、expandの方を使えばいいと思われます。

String unexpand() [since v1.7.3]

タブ文字(\t)を半角スペース8文字で置き換えます。

def expect8 = '''\
aaa
\tbbb
ccc'''

// bbbの前の半角スペース8文字をタブ文字(\t)へ
assert expect8 == '''\
aaa
        bbb
ccc'''.unexpand()

String unexpand(int tabStop) [since v1.7.3]

#unexpand()の半角スペース数を指定できるバージョン。

def expect4 = '''\
aaa
\tbbb
ccc'''

// bbbの前のタブ文字(\t)を半角スペースへ
assert expect4 == '''\
aaa
    bbb
ccc'''.unexpand(4)

String unexpandLine(int tabStop) [since v1.7.3]

行を無視して指定した数分の半角スペースをタブ文字(\t)に置き換えます。

def expect4 = '''\
aaa\t\t bbb
ccc'''

assert expect4 == 'aaa \n    bbb\nccc'.unexpandLine(4)

※このメソッドはunexpand()unexpand(int tabStop)から内部的に呼ばれているようでして
意味的にも改行を無視することになるので分かりにくくなるため、unexpandの方を使えばいいと思われます。

String stripIndent() [since v1.7.3]

文字列の各行について、行頭から最小幅で空白を取り除いてくれます。

// 期待する結果
def expect = '''\
  a
 b
c
'''
// 先頭からは何も取り除かれない
assert expect == '''\
  a
 b
c
'''.stripIndent()

// すべての行の先頭から1文字分スペースが取り除かれる
assert expect == '''\
   a
  b
 c
'''.stripIndent()

// すべての行の先頭から2文字分スペースが取り除かれる
assert expect == '''\
    a
   b
  c
'''.stripIndent()

このメソッドではタブなどは取り除かれないことに注意して下さい。

String stripIndent(int numChars) [since v1.7.3]

stripIndent()は最小幅の空白を除去しましたが、こちらは除去する文字数を指定できます。
なので空白以外でも除去可能です。

def str = '''\
@@@a
+++b
---c
'''
// 行頭から3文字除去する
assert '''\
a
b
c
''' == str.stripIndent(3)

// 行頭から4文字除去する
assert '''\



''' == str.stripIndent(4)

// 範囲を超えた場合は行が空になる
assert '''\



''' == str.stripIndent(5)

// 0より小さいと何も削除されない
assert '''\
@@@a
+++b
---c
''' == str.stripIndent(0)

String stripMargin() [since v1.7.3]

行頭からパイプ(|)までの空白文字をパイプ(|)を含めて除去します。

def expect = '''\
aaa
bbb
ccc'''

// デフォルトでは パイプ(|)を先頭とみなして
// 各行でその部分までを除去する
assert expect == '''aaa
                   |bbb
                   |ccc'''.stripMargin()

// すべての行頭にパイプ(|)を付けても構わない
assert expect == '''|aaa
                    |bbb
                    |ccc'''.stripMargin()

先頭からみていって空白以外で初めて出てきたパイプ(|)のみが対象となります。
だからたとえば、

println '''*|aaa
           *|bbb
           *|ccc'''.stripMargin()

みたいな指定は残念な結果になります。

String stripMargin(char marginChar) [since v1.7.3]

String stripMargin(char marginChar)参照。
String版の方はStringの1文字目を使ってchar版を呼び出します。ただ、Groovyの場合charにするには

'a' as char

と書かないといけないので若干面倒。気にせず1文字だけのStringで呼び出せばよい思われます。

String stripMargin(String marginChar) [since v1.7.3]

こちらはstripMargin()でパイプ以外を先頭文字に指定できるバージョンです。

def expect = '''\
aaa
bbb
ccc'''
// *を先頭文字とする例
assert expect == '''*aaa
                    *bbb
                    *ccc'''.stripMargin('*')

marginCharはStringなのですが、このメソッドの処理は実際にはcharAt(0)(先頭1文字)を
stripMargin(char marginChar)に委譲しているのみです。

assert expect == '''*aaa
                    *bbb
                    *ccc'''.stripMargin('*このあとは自由だ!')

ただ、Groovy v1.8.0RC2のソースを見てみると

// TODO IllegalArgumentException for marginChar.length() > 1 ? Or support String as marker?

などと書いてあるので、どこかのバージョンでString引数はIllegalArgumentExceptionになってしまうのかもしれません。

また、指定できるのは先頭からみていって空白以外で初めて出てきた文字のみ指定可能だということです。
だからたとえば、

println '''*:aaa
           *:bbb
           *:ccc'''.stripMargin(':')

みたいな指定は残念な結果になります。

*1:'str' のような感じで。

*2:Groovyでの正規表現についての基本的な事項は先にid:uehajさんの[http://d.hatena.ne.jp/uehaj/20070823/1200970407:title]で確認しておくと理解が早まると思います。

*3:String#padLeftとString#padRightの応用編と言えるかも知れません。