特企1-2̲Java 04.9.29 19:19 ページ154

Java 編J2SE の正規表現パッケージを学ぶ
Shibuya Perl Mongers
小山 浩之 OYAMA Hiroyuki

oyama@cpan.org

はじめに

http://module.jp/

Java の方言

J2SE（Java 2 Platform, Standard Edition） 1.4が登Javaにおける正規表現の操作に必要なオブジェクト場するまで，Java における正規表現はJakarta ORO，は，コンパイルした正規表現を表すjava.util.regex.

IBMのcom.ibm.regex，com.stevesoft.pat，gnu.regexp

Pattern と，Pattern オブジェクトが持つ正規表現の適などサードパーティが提供するパッケージに頼る必要用結果を表すjava.util.regex.Matcher，さらに不正ながありました．性能や機能面で優秀なパッケージもあ

正規表現を使用した際に発生する例外java.util.regex.

れば，バグが多くメンテナンスも活発でないパッケー

PatternSyntaxException の合計3 つだけです．正規表

ジもあるなど，利用者から見るとどれを選択すれば良現の操作には通常PatternとMatcherの2つのオブジェいのか非常にわかりにくい状況が続いていました．しクトを利用しますが，String オブジェクトに追加されかしJ2SE 1.4 から標準装備されたjava.util.regex パッ

たreplaceAllメソッドやreplaceFirstメソッドなどでもケージは，Perl ライクでモダンな機能と良好な処理性正規表現を利用した置換処理が利用できます．

能，Unicode の幅広いサポート，整備されたドキュメントなど，機能・性能・サポート・将来性などの面から安心して利用できるパッケージになっています．
本章ではJ2SE 1.4から標準装備されたjava.util.regex
パッケージの利用方法を説明します．

マッチング
String オブジェクトtext で指し示す文字列を対象にマッチングを行うには，リスト1 のように記述します．
正規表現の文字列でPatternオブジェクトを生成し，その正規表現と対象となる文字列をmatcherメソッドで関連付けMatcherオブジェクトを生成します．生成したMatcher オブジェクトのfind メソッドなどで最終▼リスト 1 Java でのマッチング
public class Ex1 {public static void main(String[] args) {String text = "Hello World!";
java.util.regex.Pattern p =java.util.regex.Pattern.compile("Hello");java.util.regex.Matcher m = p.matcher(text);

的にマッチング処理を実行します．

置換
置換を行う場合はリスト2 のようにMatcher オブジ
ェクトのreplaceFirstメソッドかreplaceAllメソッドをif (m.find()) {// textはHelloを含んでいる} else {// textはHelloを含んでいない}}}

154● WEB +DB PRESS Vol.19

使用します．replaceFirst メソッドが最初にマッチした箇所の置換結果を返すのに対し，replaceAll メソッドはマッチするすべての箇所を置換した結果を返します．リスト2を実行すると文字列「www.example.com」

特企1-2̲Java 04.9.29 19:19 ページ155

Java 編J2SE の正規表現パッケージを学ぶ▼表 1 java.util.regex.Pattern クラス▼表 2 java.util.regex.Matcher クラス

戻り値

メソッド

戻り値

メソッド

static Patterncompile(String regex)

booleanfind()

static Pattern1

compile(String regex, int flags)

booleanfind(int start)

Matchermatcher(CharSequence input)

Stringgroup(int group)

static boolean

matches(String regex,Stringgroup()

intgroupCount()

StringreplaceFirst(String replacement)

StringreplaceAll(String replacement)

Matcher

appendReplacement(StringBuffer sb,CharSequence input)
String[]split(CharSequence input)

String[]split(CharSequence input, int limit)

int

flags()

Stringpattern()▼リスト 2 Java での置換
import java.util.regex.*;public class Ex2 {public static void main(String[] args) {String text = "www.example.com";
Pattern p = Pattern.compile("\\.com");
Matcher m = p.matcher(text);
String replaced = m.replaceFirst(".jp");
System.out.println(replaced);}}

を対象に，正規表現\.comに一致する箇所を.jp に置換した結果が返され，「www.example.jp」と出力されます．
またPattern，Matcher オブジェクトを介さずにString replacement)StringBuffer

appendTail(StringBuffer sb)

intstart()

intstart(int group)

intend()intend(int group)booleanlookingAt()

booleanmatches()

Patternpattern()

Matcherreset()

Matcherreset(CharSequence input)▼表 3 java.util.regex のメタ文字注 2
メタ文字

役割

\p{Prop}

Propで指定したUnicodeプロパティとブロック（スクリプトは未サポート）

StringオブジェクトのreplaceAllメソッドやreplaceFirstメソッドでも正規表現を使用した置換を行えます．
PatternおよびMatcherクラスのメソッドを表1，表\P{Prop}

Propで指定したUnicodeプロパティとブロック以外?+0もしくは1文字（強欲な量指定子）

2 それぞれに示します．各メソッドの詳細については*+0文字以上の繰り返し（強欲な量指定子）

Javadocを参照してください注1．

++

1文字以上の繰り返し（強欲な量指定子）{n}+n個の繰り返し（強欲な量指定子）{n,}+n個以上の繰り返し（強欲な量指定子）{n,m}+

n個以上m個以下の繰り返し（強欲な量指定子）

メタ文字
java.util.regex は第1 章 表1 に挙げた現在一般的な
正 規 表 現 のほとんどの機 能 を利 用 できます．
java.util.regex ではさらに*+や++や?+といった強欲を\ でエスケープした上で指定する必要があります．

な量指定子や，文字クラスの集合演算などが利用でjava.util.regex の特徴的な機能として強欲な量指定

きます（表3）．

子が挙げられます．通常のマッチング処理ではさまざ正規表現はStringオブジェクトで与えるため，ソーまなマッチの可能性に備え，検索経路の状態（ステースコード中で\w+\sなどのメタ文字を記述する場合ト）を記憶しています．強欲な量指定子はこのマッチは，\\w+\\sといったふうに \（バックスラッシュ）中の状態を生成せず，マッチした範囲を強欲に保持し

注 1）http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/package-summary.html
注 2）この他の一般的なメタ文字については第 1 章 表 1 と java.util.regex.Pattern のドキュメントを参照してください．

WEB +DB PRESS Vol.19 ●155

特企1-2̲Java 04.9.29 19:19 ページ156

特別企画

1

文字列処理はこれでバッチリ！て解放しません．たとえば文字列 12345 に正規表現などのUnicode ブロックを使用します．

^(\d+)\d$を使用するとキャプチャ結果1234を得る

Unicode ブロックを指定する場合はブロック名の接ことができますが，強欲な量指定子^(\d++)\d$の頭子としてInを付加し，下線や空白を含まない形式

場合は12345 すべての文字列を\d++が押さえるためでブロックを指定します．たとえばUnicodeで0000かに最終的にマッチしなくなります．

ら007F のブロックをさす名前としてBasic Latin が定Unicodeに関係する要素として\w，\d，\sおよび\W，\D，\SはASCII 文字だけにマッチし，Unicode義されていますが，正規表現でこのブロックを指定する場合は\p{InBasicLatin}と記述します注3．

におけるほかの英数字や空白などにはマッチしない点

UnicodeブロックはUnicode文字マップ内の特定のに注意が必要です．Unicode上の英数字にマッチさせ範囲（ブロック）を表すものであるのに対し，Unicodeたい場合は\p{L}，Unicode 上の数字にマッチさせスクリプトは言語別の書記体系（スクリプト）をグルたい場合は\p{Nd}，空白文字にマッチさせたい場合ープ化したものです．そのためUnicode スクリプトがは\p{Z}を使用します．ちなみに，プロパティ名はサポートされていなくとも，Unicode ブロックや\p{Lu}といった短縮系の名前だけをサポートし，Unicode プロパティの組み合わせで期待する文字を指\p{Lowercase_Letter}といった長い名前はサポ定することができますが，Unicode ブロックはあくまートしていない点にも注意が必要です．

でも文字マップ上の特定の範囲を指すものですので，java.util.regexではUnicodeプロパティとブロックをほぼ完全にサポートしていますが，Unicode スクリプ

実際に文字が割り当てられていない領域を含む場合がある点に注意が必要です．

トはサポートしていません．そのため日本語のひらがオプション

な・カタカナ・漢字 を指す Unicode スクリプト\p{Hiragana}・ \p{Katakana}・ \p{Han}は正規表現のオプションはPattern.compile メソッド

使用できないので，代わりにひらがな・カタカナ・漢の第2引数か，正規表現中の(?mode)で指定します．

字のコードブロックを指す\p{InHiragana}・\p{java.util.regexで使用できるオプションを表4に示しまInKatakana}・\p{InCJKUnifiedIdeographs}

す．
java.util.regex ではUnicode のサポートに際し，行▼表 4 java.util.regex のオプション
オプション(?mode)

役割

Pattern.CASE_INSENSITIVEi大文字と小文字を無視する

Pattern.MULTILINEm

行単位でマッチングする

Pattern.DOTALLs.（ドット）を改行文字にもマッチ

Pattern.UNIX_LINESd

Pattern.COMMENTS

Pattern.CANON_EQ

x（なし）¡CR（U+000D）¡CRLF（U+000D U+000A）¡NEL（U+0085）^（ハット）を\nだけに反応させ¡LS（U+2028）

る（.の動作も変化する）¡PS（U+2029

正規表現中の空白やコメントを許可するの6 種類をサポートしています．これらの文字は行の通常は同じ文字として扱われなが先頭を指す^（ハット），行の末尾を指す$ や，.（ドなる文字を，同じ文字としてマッu¡LF（U+000A）

させる

ら実際にはエンコーディングの異Pattern.UNICODE_CASE

末を示す終端子としてット）や\Zに作用します．これらのメタ文字が6 種チングする類の行終端子のどれに作用するかは表4 で挙げたオプ

Unicodeにおける大文字・小文字

ションUNIX_LINES，DOTALL，MULTILINEの組を無視するみ合わせで変化するので注意が必要です．

注 3）その他のブロック名とその範囲は http://www.unicode.org/Public/UNIDATA/Blocks.txt を参照してください．

156● WEB +DB PRESS Vol.19

特企1-2̲Java 04.9.29 19:19 ページ157

Java 編J2SE の正規表現パッケージを学ぶ
番号は7 桁の数字で，3 桁目と4 桁目の間にハイフンキャプチャを入れて表記します注4．また，旧郵便番号には3 桁のマッチした文字の一部分を取り出したり検索結果にものと5 桁のものがあります．通常は現在利用されて埋め込みたい場合は，正規表現のキャプチャを使用しいる7 桁の郵便番号を入力させる場合が多いと思いまます．リスト3 のように正規表現中でキャプチャ用括すが，ここでは弧 ( と ) で指定した範囲にマッチした文字列は，Matcher.group(int num)メソッドで参照できます．
キャプチャ用括弧が複数ある場合は，括弧の出現¡154¡350-11¡350-1106

順に応じてm.group(1)，m.group(2)，m.group(3)…と取得したいインデックス番号を与えることでの3 パターン，およびハイフンを省略した郵便番号文個別にアクセスできます．またgroup メソッドで指定字列を許可する以下の正規表現を用意します（リスできる値の最大値はMatcher.groupCount( )メソッド

ト5）．

で得ることができます．
replaseFirstメソッドやreplaceAllメソッドでの文字列置換の際にキャプチャを利用する場合は，リスト4のようにキャプチャ結果を埋め込みたい位置にキャプ^\\d{3}(?:-?(?:\\d{2}|\\d{4}))?$

もし7 桁の郵便番号のみを許可する場合は以下の正規表現を使用します．

チャ用括弧の出現順に応じて$1，$2，$3…などの文字を指定します．リスト4を実行すると「Here... (url:http://www.example.jp)」と出力されます．

^\\d{3}-?\\d{4}$

5 桁と7 桁の郵便番号を許可してハイフンの省略をjava.util.regex による基本的な正規表現の利用手順

許可しない場合は以下の正規表現を使用します．

は以 上 のとおりです． この他 にもMatcher オブジェクトには，置換結果 ▼リスト 3 キャプチャの例
をStringBufferオブジェクトに詰め込んimport java.util.regex.*;でいくappendReplacementメソッドな

public class Ex3 {public static void main(String[] args) {String text = "Name: Regular Expressions";
Pattern p = Pattern.compile("^(\\w+):\\s+(.+)$");
Matcher m = p.matcher(text);

ど，パフォーマンス的に有効なメソッドがいくつか用意されています．

if (m.find()) {String key= m.group(1); // key= "Name";String value = m.group(2); // value = "Regular Expressions";
System.out.println("key= " + key);
System.out.println("value = " + value);}

利用例
それではWeb アプリケーション開発}に際して頻繁に発生する課題を例に， }java.util.regexのより具体的な利用例を ▼リスト 4 キャプチャ結果による置換
見ていきましょう．

データのチェック
正規表現によるデータのチェックの例として，郵便番号のチェックを見てみましょう．現在利用されている郵便

import java.util.regex.*;public class Ex4 {public static void main(String[] args) {String text = "<a href=\"http://www.example.jp/\">Here...</a>";String replaced = text.replaceFirst("<a href=\"(.+?)\">(.+?)</a>", "$2 (url: $1)");
System.out.println(replaced);}}

注 4）現在は算用数字のみが利用されていますが，郵便番号制マニュアルには「当分の間，算用数字のみを使用します．」と記載されているので，ライフサイクルの長いプログラムでは注意が必要かもしれません．http://www.post.japanpost.jp/zipcode/zipmanual/

WEB +DB PRESS Vol.19 ●157

特企1-2̲Java 04.9.29 19:19 ページ158

特別企画

1

文字列処理はこれでバッチリ！
^\\d{3}-(?:\\d{2}|\\d{4})$ひらがな・カタカナ・漢字
java.util.regexではUnicodeプロパティとUnicodeブ

つまりひらがなの文 字 列 にマッチさせる場 合 は\\p{InHiragana}+，カタカナの場合は\\p{InKatakana}+，漢字の場合は\\p{InCJKUnified
Ideographs}+を使用します．

ロックがサポートされています．そのため日本語に関リスト6 は文字列から英数字・ひらがな・カタカ

係するひらがなやカタカナもしくは漢字などのマッチナ・漢字の各文字種ごとにブロックを抽出するサンプングを行う場合は，Unicode ブロックを使用します．

ルです．
リスト6 を実行すると▼リスト 5 郵便番号

% java Ex6

import java.util.regex.*;漢字
public class Ex5 {public static void main(String[] args) {String text = args[0];String pcode_reg = "^\\d{3}(?:-?(?:\\d{2}|\\d{4}))?$";
if (Pattern.matches(pcode_reg, text)) {System.out.println(text + " is postal code");} else {System.out.println(text + " is not postal code");
}
}

カナ
混
じりの文章をinput}

します▼リスト 6 文字種ごとの抽出
import java.util.regex.*;と分割した文字列を逐次出力します．日本語を含

public class Ex6 {public static void main(String[] args) {String text = "漢字カナ混じりの文章をinputします";
Pattern p = Pattern.compile("(\\p{InBasicLatin}+|" +" \\p{InHiragana}+|" +" \\p{InKatakana}+|" +" \\p{InCJKUnifiedIdeographs}+)",
Pattern.COMMENTS);
Matcher m = p.matcher(text);

む文字列の簡単なインデックスを作る場合や，文while (m.find()) {String chunk = m.group(1);System.out.println(chunk);}}

章中の漢字とひらがなの含有率を調べる場合などに便利です注5．

テキスト中の URL をハイパーリンクに置換する
WeblogやWebメールなどの実装では，入力された文章に含まれるURLをハイパーリンクとしてマークアップする場面がよく現れます．正規表現の置換を利用すれば，この類いの面倒な処理も簡素に記}述できます．リスト7 はURL を単純化し▼リスト 7 URL のマークアップ

た正規表現を使って，URLをaタグでマー

import java.util.regex.*;public class Ex7 {public static void main(String[] args) {String text = "httpやhttpsのスキームを持つ"+ "http://www.example.jp/path/to/file.htmlといったURLを"+ "マークアップします．";
String replaced = text.replaceAll("(https?://[-_.!˜*'()a-zA-Z0-9;/?:@&=+$,%#]+)","<a href=\"$1\">$1</a>");System.out.println(replaced);}

クアップします．
リスト7 を実行すると文中に含まれる「http://www.example.jp/path/to/file.
html」というURL を，<a href="http://www.example.jp/path/to/file.html">http://www.example.
jp/paht/to/file.html</a>（実際は1行）}

注 5）同様の処理を Perl の Unicode スクリプトで記述した例と，この他の Unicode プロパティなどの概要については http://module.jp/blog/regex̲unicode̲prop.html を参照してください．

158● WEB +DB PRESS Vol.19

特企1-2̲Java 04.9.29 19:19 ページ159

Java 編J2SE の正規表現パッケージを学ぶ
らの不満は解消されつつあります．java.util.regex はに置換します注6．

かなりの部分で最新のPerl に迫る（一部は追い越しHTML タグを取り除くた）機能と完成度の正規表現パッケージです．これか

クロスサイトスクリプティング防止や，表示上の一らはどの正規表現パッケージを選べば良いのかを悩む

貫性を維持する目的などで，HTML の文書からタグ必要はなく，特に理由がない限り，標準添付され，必を取り除いたテキストだけを取り出したい場合があり要十分な機能と性能を備え，ドキュメントが整備さます．ここでは単純に < と > でくくられた要素を削れ，将来的にもメンテナンスされ続けるであろう

除 す る だ け で す の で String.indexOf( )や String.

java.util.regexを選べばよいのです．

substring( )メソッドなどでも処理できますが，正規今まで正規表現を使ったことがない方も，今まで正表現を使えばリスト8 のように処理の内容がより明確規表現が使えないためにJavaを避けていた方も，ぜひになります．

一度java.util.regex のキモチヨサを体感してみてくだ
さい．#

XML 文書を解析する
通常Java におけるXML の処理はjavax.xml.parsers
やサードパーティが供給するXML 用のパッケージを使用しますが，必要な情報を抽出するだけであれば正規表現でも▼リスト 8 HTML タグを取り除く
import java.util.regex.*;public class Ex8 {public static void main(String[] args) {String text = "<p>今週の<a href=\"/to/file\">特選品</a>！</p>";

簡単に記述できます．リスト
String replaced = text.replaceAll("<.+?>", ""); // "<[^>]+>"でも可System.out.println(replaced);

9はWebサイトのコンテンツの一覧と内容の配信に使用されるRSS（RDF Site Summary）
ファイルを解析し，新しい記事上位 5 件のタイトルとURLをリストアップします．

まとめ}}▼リスト 9RSS の解析

import java.io.*;import java.util.regex.*;public class Ex9 {public static void main(String[] args) {BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
StringBuffer rss = new StringBuffer("");
String line;
try {while ((line = r.readLine()) != null) {rss.append(line);}} catch (IOException e) {e.printStackTrace();}

今までは安心して採用できる正規表現パッケージがJavaにはなかったため，文字列処理での手数の多さやまどろっ

Pattern p = Pattern.compile("(?:<item>|<item\\s.+?>)" +".*?<title>(.*?)</title>" +".*?<link>(.*?)</link>" +".*?</item>", Pattern.COMMENTS);
Matcher m = p.matcher(rss);for (int i = 0; m.find() && i < 5; i++) {System.out.println(m.group(1) + "(" + m.group(2) + ")");}

こしさからJava を採用しないプログラマも多かったことと思います．しかしバージョン1.4
か ら 標 準 採 用 さ れ た
java.util.regexによって，これ}}

注 6）URL のより正確な正規表現は大崎博基さんの Web サイトの「Perl メモ」→「http URL の正規表現」を参照してください．他にも有用な正規表現の例と考察がたくさん掲載されています．http://www.din.or.jp/˜ohzaki/

WEB +DB PRESS Vol.19 ●159