2011年5月31日火曜日

Java正規表現

正規表現とは文字列の一部をパターン化して記述する表現方法の事になります。例えば、「.」を任意の文字、「\d」を数字として表す事が出来ます。 もとは UNIX awkなどのテキストフィルタで用いられており、Perlを使ったことがある方にとっては馴染み深いかもしれません。Javaでも JDK1.4から、java.util.regex パッケージが導入され、正規表現のクラスが標準で使用できるようになりました。

java.util.regex
パッケージはPatternクラスMatcherクラスで構成されています。 Patternクラスでは正規表現をコンパイルし、正規表現処理を行うMatcherクラスのオブジェクトを生成します。

Matcher
クラスでは正規表現処理(文字列が正規表現にマッチするか、正規表現にマッチした文字列の置換など)を行うメソッドが用意されています。

vb.txt
perl.txt
c.txt
java.txt


正規表現ではこれらをまとめて以下のように表現できる。

*.txt


上の例では [ *.txt ] が左の4つの文字列を表わす正規表現となります。 またこのように正規表現で表現出来る事をマッチすると言います。

 

正規表現構文

 

正規表現とは文字列の一部をパターン化して記述する表現方法の事になります。例えば、「*」を任意の文字列、「?」を任意の 1文字として表す事が出来ます。 もとは UNIX awkなどのテキストフィルタで用いられていましたが、Perlではとても強力な正規表現をサポートしています。

[
以下のような文字列を・・]

vb.txt
perl.txt
c.txt
java.txt


[
次のようにまとめて表現できる]

*.txt


この例では [ *.txt ] が左の4つの文字列を表わす正規表現となります。 またこのように正規表現で表現出来る事をマッチすると言います。

上の例で出てくる [ . ] は定義されている正規表現構文と呼ばれます。 この正規表現構文は正規表現では一つで英数字を表現したり、または英数字以外を表現したりとても重要な役割を持っています。 これから代表的な正規表現構文の使い方を見ていきます。 下に [ 正規表現構文表 ] もありますので参照して下さい。

任意の一文字 "."

 

改行文字を除く任意の1文字


[
表現 ]

windows..


[
結果 ]

windowsNT    # 全てマッチ
windows98    #
全てマッチ
windows3.1    # windows3.
までマッチ



くり返し表現 " * " , " ? " , " + "



 前で説明した任意の1文字では上の全てをマッチする事は出来ませんでした。しかしこれから説明する繰り返し表現を使用する事により、ある文字や文字列の繰り返しという複雑な表現をする事が出来ます。

" * "前にある文字の0回以上の表現


[
表現 ]

12*


[
結果 ]

1222222222    # 全てマッチ
1                    #
0回もマッチ

 

" ? "前にある文字の0回または1回の表現


[
表現 ]

12?


[
結果 ]

1222222222    # # 12のみマッチ
1                    #
0回もマッチ

 

" + "前にある文字の1回以上の表現


[
表現 ]

12+


[
結果 ]

1222222222    # 全てマッチ
1                    # 0
回なのでマッチしない

 

*または+を指定した場合は最長マッチとなります


[
表現 ]

12+


[
結果 ]

1222222222    # 全てマッチ

 

量指定のメタキャラに"?"を付加する事によって最小マッチとなる


[
表現 ]

12+?


[
結果 ]

1222222222    # 2桁目までマッチ



指定内の任意の表現 " [ ] "



 任意の二つの文字にマッチさせたい時や、複数の文字の中のいずれかにマッチさせたいと言った場合は " [ ] " で複数の候補を指定する事ができます。 下記のように指定することで、 a 又は b でマッチします。

[ a
又は b にマッチする ]

[ab]



 小文字アルファベットであればマッチする表現を考えてみてください。 [ ] を使えば、 [abcd...z] で表現する事が出来ます。 しかし実際これでは非常長い表現になってしまい使い勝手が悪く、さらにはスペルミスも起こしかねません。 こんな時のために、「 小文字のアルファベット全て」 といった表現を省略して簡単に使う事が出来ます。(下図参照)

表現

意味

[a-z]

a-zの小文字アルファベット全てを表わす

[0-9]

数値1桁を表わす

[A-Z]

A-Zの大文字アルファベット全てを表わす

[^a-z]

小文字のアルファベット以外全てを表わす

[a-z&&[^bc]]

b c を除く a zを表わす



 これまで紹介した例はある文字を一つを表現する方法でした。 下記のサンプルは 1文字だけではなく 文字列を表現する方法です。 文字列は [ | ] で区切って表現します。

[
表現 ]

(this|that) is sample


[
結果 ]

this is sample 又は that is sample にマッチします。



回数指定の表現



 今まで見てきたメタキャラでは 0回以上や 1回以上といった指定しか出来ませんでした。 この様な場合 perlでは なるべくたくさんマッチしようとする [ 最長マッチ ] になりますが、?回にマッチや任意の数以上にマッチさせる回数指定の方法を見てみます。

表現

意味

{a}

直前の文字の a回にマッチ

{a,}

直前の文字の a回以上にマッチ

{a,b}

直前の文字の a回以上 b回以下にマッチ

{a,}?

?マークが付く事で最短を表現できる

{a,b}?

?マークが付く事で最短を表現できる



[
表現 ]

#[aaa] のみにマッチ
a{3}

#[aaa] , [aaaaaaaa]
など3回以上連続する [a] にマッチ
a{3,}

 

位置の指定 [^] [$]



 今回は〜から始まる単語、〜で終わるといった単語のの表現方法になります。 〜から始まる単語という表現は単語の初めに [ ^ ] を付けます。 逆に 〜で終わっている単語と言う表現は、単語の最後に [ $ ] を付けます。

[
表現 ]

#"abc"で始まっていたら
^abc

#"abc"
で終わっていたら
abc$



[
使用例 ]

W.*[T8]$

Windows 95         #
マッチしない
Windows 98         #
マッチ
Windows NT         #
マッチ


上記の正規表現は先頭が [ W ] で始まっていて最後が [ T ] 又は [ 8 ] で終わっていると言うことを表現しています。 そのため上の3つのサンプルでは先頭は全て [ W ] から始まっているので当てはまります。 しかし 最後が [ T ] 又は [ 8 ] と言う条件を "Windows 95" だけ満たしていませんのでマッチしないという訳です。

正規表現構文の要約

文字

x

文字 x

\\

バックスラッシュ文字

\0n

8 進値 0n を持つ文字 (0?<=?n?<=?7)

\0nn

8 進値 0nn を持つ文字 (0?<=?n?<=?7)

\0mnn

8 進値 0mnn を持つ文字 (0?<=?m?<=?30?<=?n?<=?7)

\xhh

16 進値 0xhh を持つ文字

\uhhhh

16 進値 0xhhhh を持つ文字

\t

タブ文字 ('\u0009')

\n

改行文字 ('\u000A')

\r

キャリッジリターン文字 ('\u000D')

\f

用紙送り文字 ('\u000C')

\a

警告 (ベル) 文字 ('\u0007')

\e

エスケープ文字 ('\u001B')

\cx

x に対応する制御文字

文字クラス

[abc]

ab、または c (単純クラス)

[^abc]

abc 以外の文字 (否定)

[a-zA-Z]

a z または A Z (範囲)

[a-d[m-p]]

a d、または m p: [a-dm-p] (結合)

[a-z&&[def]]

def (交差)

[a-z&&[^bc]]

b c を除く a z: [ad-z] (減算)

[a-z&&[^m-p]]

m p を除く a z: [a-lq-z] (減算)

定義済みの文字クラス

.

任意の文字 (行末記号とマッチする場合もある)

\d

数字: [0-9]

\D

数字以外: [^0-9]

\s

空白文字: [ \t\n\x0B\f\r]

\S

非空白文字: [^\s]

\w

単語構成文字: [a-zA-Z_0-9]

\W

非単語文字: [^\w]

境界正規表現エンジン

^

行の先頭

$

行の末尾

\b

単語境界

\B

非単語境界

\A

入力の先頭

\G

前回のマッチの末尾

\Z

最後の行末記号がある場合は、それを除く入力の末尾

\z

入力の末尾

最長一致数量子

X?

X1 回または 0

X*

X0 回以上

X+

X1 回以上

X{n}

Xn

X(n,}

Xn 回以上

X{n,m}

Xn 回以上、m 回以下

最短一致数量子

X??

X1 回または 0

X*?

X0 回以上

X+?

X1 回以上

X{n}?

Xn

X(n,}?

Xn 回以上

X{n,m}?

Xn 回以上、m 回以下

強欲な数量子

X?+

X1 または 0

X*+

X0 回以上

X++

X1 回以上

X{n}+

Xn

X(n,}+

Xn 回以上

X{n,m}+

Xn 回以上、m 回以下

論理演算子

XY

X の直後に Y

X|Y

X または Y

(X)

X、前方参照を行う正規表現グループ

前方参照

\n

マッチした n 番目の前方参照を行う正規表現グループ



(
注1
 デフォルトでは、数量子を付けたサブパターンは、「最長一致」となります、これは残りのパターンがマッチしなくならない範囲で、できる限り多くのものにマッチすると言う事です。つまり、標準の数量子は、すべて「最長一致」で、パターンが失敗しない範囲で可能な限り長い範囲にマッチさせます。

 もし、最短の範囲でマッチさせたい時は、数量子の後に " ? "を付けると「 最短一致 」にする事が出来ます。( 意味自体は変りません

Javaでの使い方「マッチする文字列を検索」

 

  ここまで説明してきた正規表現を利用しマッチする語句が含まれているか調べるには次のような記述になります。 文字列にパターンマッチする語句が含まれていれば [ ] となる値を、含まれていなければ [ ] となる値を返します。

Helloから始まっているか・・

public class RegexSample {
    public static void main(String args[]){
 
        Pattern pattern = Pattern.compile("^Hello");
        Matcher matcher = pattern.matcher("Hello World. This is regex sample.");
        boolean blnMatch= matcher.matches();
        System.out.println(blnMatch);
    }
 
}

 [出力]

true

 上記の表現は「Hello」で始まっているか?を表します。そのため「Hello World. This is regex sample.」はその条件にマッチするので、結果は「真」となります。

 

Javaでの使い方「マッチする文字列を置換え」

 

 上記では文字列が含まれているかをチェックしましたが、今度はマッチした文字列を置換するやり方を見てみます。置換には Matcherクラスの「replaceFirst」メソッド、「replaceAll」メソッドを使用します。

文字列の置換(最初にマッチしたもの)

public class RegexSample {
    public static void main(String args[]){
 
        Pattern pattern = Pattern.compile("Hello");
        Matcher matcher = pattern.matcher("Hello World. Hello World.");
        String strResult = matcher.replaceFirst("Good");
        System.out.println(strResult);
 
    }
 
}

 [結果]

Good World. Hello World.

文字列の置換(一括)

public class RegexSample {
    public static void main(String args[]){
 
        Pattern pattern = Pattern.compile("Hello");
        Matcher matcher = pattern.matcher("Hello World. Hello World.");
        String strResult = matcher.replaceAll("Good");
        System.out.println(strResult);
 
    }
 
}

 [結果]

Good World. Good World.

 上記のように「replaceAll」メソッドを使用した場合、マッチした全ての文字列を置換することが出来ます。

 

0 件のコメント:

コメントを投稿