Created
August 8, 2013 10:27
-
-
Save ryo-utsunomiya/6183541 to your computer and use it in GitHub Desktop.
blog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | |
<style> | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6, | |
p, | |
blockquote { | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif; | |
font-size: 13px; | |
line-height: 18px; | |
color: #737373; | |
background-color: white; | |
margin: 10px 13px 10px 13px; | |
} | |
table { | |
margin: 10px 0 15px 0; | |
border-collapse: collapse; | |
} | |
td,th { | |
border: 1px solid #ddd; | |
padding: 3px 10px; | |
} | |
th { | |
padding: 5px 10px; | |
} | |
a { | |
color: #0069d6; | |
} | |
a:hover { | |
color: #0050a3; | |
text-decoration: none; | |
} | |
a img { | |
border: none; | |
} | |
p { | |
margin-bottom: 9px; | |
} | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6 { | |
color: #404040; | |
line-height: 36px; | |
} | |
h1 { | |
margin-bottom: 18px; | |
font-size: 24px; | |
} | |
h2 { | |
font-size: 18px; | |
} | |
h3 { | |
font-size: 16px; | |
} | |
h4 { | |
font-size: 15px; | |
} | |
h5 { | |
font-size: 14px; | |
} | |
h6 { | |
font-size: 13px; | |
} | |
hr { | |
margin: 0 0 19px; | |
border: 0; | |
border-bottom: 1px solid #ccc; | |
} | |
blockquote { | |
padding: 13px 13px 21px 15px; | |
margin-bottom: 18px; | |
font-family:georgia,serif; | |
font-style: italic; | |
} | |
blockquote:before { | |
content:"\201C"; | |
font-size:40px; | |
margin-left:-10px; | |
font-family:georgia,serif; | |
color:#eee; | |
} | |
blockquote p { | |
font-size: 14px; | |
font-weight: 300; | |
line-height: 18px; | |
margin-bottom: 0; | |
font-style: italic; | |
} | |
code, pre { | |
font-family: Monaco, Andale Mono, Courier New, monospace; | |
} | |
code { | |
background-color: #fee9cc; | |
color: rgba(0, 0, 0, 0.75); | |
padding: 1px 3px; | |
font-size: 12px; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
} | |
pre { | |
display: block; | |
padding: 14px; | |
margin: 0 0 18px; | |
line-height: 16px; | |
font-size: 11px; | |
border: 1px solid #d9d9d9; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
} | |
pre code { | |
background-color: #fff; | |
color:#737373; | |
font-size: 11px; | |
padding: 0; | |
} | |
@media screen and (min-width: 914px) { | |
body { | |
width: 854px; | |
margin:10px auto; | |
} | |
} | |
@media print { | |
body,code,pre code,h1,h2,h3,h4,h5,h6 { | |
color: black; | |
} | |
table, pre { | |
page-break-inside: avoid; | |
} | |
} | |
</style> | |
<title>正規表現入門(2) - メタ文字・文字クラス・実行モード</title> | |
</head> | |
<body> | |
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> | |
<script> | |
$(function(){ | |
$(".regsub").css("background", "#eee").css("padding", "0.1em 0.5em"); | |
//$(".regfield").css("border", "solid 1px #ccc").css("padding", "0.5em").css("lineHeight", "3"); | |
}); | |
</script> | |
<p>こんにちは。宇都宮です。夏〜秋にかけては、プログラミング言語のカンファレンスがいくつかありますね。私は<a href="http://ll.jus.or.jp/2013/">LLまつり</a>と<a href="http://phpcon.php.gr.jp/w/2013/">PHPカンファレンス</a>に参加予定です。<a href="http://yapcasia.org/2013/">YAPC::Asia</a>も参加するかも…。</p> | |
<p><a href="http://blog.asial.co.jp/1171">前回の記事</a>は「正規表現に触ってみる」がテーマでした。今回は、正規表現の基本である、メタ文字と文字クラス、そして実行モードについて学んでいきます。</p> | |
<h3 class="title-h3">メタ文字とリテラル</h3> | |
<p>正規表現を構成する文字には、メタ文字とリテラルの2種類があります。</p> | |
<p>メタ文字とは、文字本来の意味とは異なる、特別な意味を持つ文字のことです(「メタ文字」とは、「文字を超えた文字」という意味)。</p> | |
<p>例えば、日本語で「○○さん」という書き方をすることがあります。「○」は円形を表す文字ですが、「○○さん」という表現の「○○」には、「任意の人名が入る場所」という特別な意味があります。</p> | |
<p>このように、メタ文字はその文字本来の意味を超えた、特別な意味を持ちます。</p> | |
<p>これに対して、リテラルとは、特別な意味を持たない文字のことです(「literal」という英語には「文字通りの」という意味があります)。</p> | |
<p>以下、正規表現のメタ文字について解説していきます。</p> | |
<h3 class="title-h3">前回のおさらい</h3> | |
<p>最初に、前回使用したメタ文字について、簡単におさらいします。</p> | |
<p>チェック対象文字列:<span id="subject00" class="regsub">123C</span></p> | |
<p>正規表現:/<input type="text" name="input" id="input00" value="-*.+">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('00', 'gm')"></p> | |
<p>結果:<span id="out00"></span></p> | |
<p>「-*.+」という正規表現は、リテラルの-、「0回以上の繰り返し」の*(アスタリスク)、「任意の1文字」の.(ドット)、「1回以上の繰り返し」の+(プラス)の4文字からなります。</p> | |
<p>「*」は0文字以上なので、「-」はあってもなくてもOK。そのため、検索対象文字列に「-」は含まれていませんが、マッチしています。「.+」は、「任意の1文字以上の繰り返し」なので、1文字以上のどんな文字列にでもマッチします。</p> | |
<h3 class="title-h3">行頭と行末</h3> | |
<p>次に、「cat」という文字列にマッチする正規表現を考えます。一番単純な正規表現は、「cat」。 c、a、t の3文字のリテラルから成る正規表現です。</p> | |
<p>チェック対象文字列:<span id="subject01" class="regsub">cat</span></p> | |
<p>正規表現:/<input type="text" name="input" id="input01" value="cat">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('01', 'gm')"></p> | |
<p>結果:<span id="out01"></span></p> | |
<p>では、以下の場合はどうでしょう。</p> | |
<p>チェック対象文字列:<span id="subject02" class="regsub">vacation</span></p> | |
<p>正規表現:/<input type="text" name="input" id="input02" value="cat">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('02', 'gm')"></p> | |
<p>結果:<span id="out02"></span></p> | |
<p>このように、「cat」という正規表現では、c、a、tという文字の並びが含まれている場合には必ずマッチしてしまいます。</p> | |
<p>では、厳密に「cat」だけを見つけたいときはどうすればよいのでしょう?</p> | |
<p>いくつかやり方はありますが、まずは以下の文字列の中から、「cat」だけを含む行を見つけるやり方を紹介します。</p> | |
<pre class="regsub" id="subject03"> | |
cat | |
vacation | |
category | |
navicat | |
</pre> | |
<p>正規表現には、「行頭」「行末」にマッチするメタ文字があります。^(キャレット)と$(ドル記号)です。^は行頭、$は行末にマッチします。まずは行頭にマッチする^を、実際に試してみましょう。</p> | |
<p>正規表現:/<input type="text" name="input" id="input03" value="^cat">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('03', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out03"></pre> | |
<p>行頭、c、a、t という文字が並んでいる、「cat」と「category」にマッチしました。次に、行末にマッチする$を使ってみます。</p> | |
<pre class="regsub" id="subject04" style="display:none;"> | |
cat | |
vacation | |
category | |
navicat | |
</pre> | |
<p>正規表現:/<input type="text" name="input" id="input04" value="cat$">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('04', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out04"></pre> | |
<p>最後に、「^cat$」を使ってみます。</p> | |
<pre class="regsub" id="subject05" style="display:none;"> | |
cat | |
vacation | |
category | |
navicat | |
</pre> | |
<p>正規表現:/<input type="text" name="input" id="input05" value="^cat$">/gm</p> | |
<p><input type="button" value="チェックする" onclick="check('05', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out05"></pre> | |
<p>では、以下の場合はどうでしょう。(インプットボックスの右のボタンを押すと、正規表現が切り替わります)</p> | |
<p><span class="regsub" id="subject06"> | |
I had a vacation. I saw a pretty cat. | |
</span></p> | |
<p>正規表現:/<input type="text" name="input" id="input06" value="^cat$">/gm | |
<button onclick="$('#input06').val('^cat$')">^cat$</button> | |
<button onclick="$('#input06').val('^cat')">^cat</button> | |
<button onclick="$('#input06').val('cat$')">cat$</button> | |
<button onclick="$('#input06').val('cat')">cat</button> | |
<button onclick="$('#input06').val('\\bcat\\b')">\bcat\b</button></p> | |
<p><input type="button" value="チェックする" onclick="check('06', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out06"></pre> | |
<p>^は行頭、$は行末にマッチするため、行頭・行末に登場する文字列にしか使えません。この場合、単語の境界を表すメタ文字を使います。「\bcat\b」を試してみてください。「cat」にだけマッチして、「vacation」にはマッチしないはずです。</p> | |
<p>「\b」は単語の境界にマッチするメタ文字です(上記例では、半角スペースとドット)。「\b」は、2つの文字を組み合わせていますが、「2文字で1文字」と考えてください。「b」は文字通りの「b」ですが、「b」の前に「\」を置くことで、「\b」=「単語の境界」という意味をもたせています。</p> | |
<p>一点注意。単語の境界を表すメタ文字は、正規表現実装によって異なります。JavaScriptやPHP、Perlなどでは「\b」が単語境界になりますが、egrepのように「\<」「\>」を用いる正規表現実装もあります。</p> | |
<h3 class="title-h3">文字クラス</h3> | |
<p>次に、以下のようなお題を考えてみます。</p> | |
<p>Q.以下の文字列の中から、HTMLの見出しタグ(<h1>〜<h6>)だけをハイライトしたい。</p> | |
<pre class="regsub" id="subject07"> | |
<h1>heading</h1> | |
<hoge> | |
<h2>fuga</h2> | |
</pre> | |
<p>いくつか書き方はありますが、ここでは「文字クラス」という正規表現の構文を使ってみましょう。</p> | |
<p>正規表現:/<input type="text" name="input" id="input07" value="h[123456]">/gm | |
<button onclick="$('#input07').val('h[123456]')">h[123456]</button> | |
<button onclick="$('#input07').val('h[1-6]')">h[1-6]</button> | |
<button onclick="$('#input07').val('h')">h[1-6</button></p> | |
<p><input type="button" value="チェックする" onclick="check('07', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out07"></pre> | |
<p>正規表現「h[123456]」のうち、hはリテラル、[123456]が文字クラスです。「[」という記号は、「]」で閉じている場合に限り、メタ文字として機能します。[123456]という文字クラスは、「1、2、3、4、5、6のいずれか1文字」という意味です。</p> | |
<p>また、[123456]の代わりに、[1-6]と書いて、「1から6までのいずれか1文字」を指定することもできます。-を使った範囲指定は、数字とアルファベットで行うことができ、[a-zA-Z0-9]といった指定を行うこともできます(正規表現は、デフォルトでは、大文字と小文字を区別します)。</p> | |
<p>文字クラスは、「この範囲の文字の中のどれか」以外に、「この範囲の文字を除くどれか」という指定もできます。以下のような構文になります。</p> | |
<p>[^abc]</p> | |
<p>「^」は「行頭」にマッチするメタ文字じゃないの? と思われた方もいるでしょう。実は、文字クラスの中と外では、「どの文字がメタ文字になるか」という規則が異なるのです。「^」は正規表現では行頭にマッチするメタ文字ですが、文字クラスの中で、文字クラスの開始記号「[」の直後に置かれると、文字クラスの意味を反転させる、という働きを持つメタ文字になります。</p> | |
<pre class="regsub" id="subject08"> | |
0123456789 | |
abcdefgHIJK | |
ABCDEFGHIJK | |
!@#$%^*()_+- | |
あいうえお | |
</pre> | |
<p>正規表現:/<input type="text" name="input" id="input08" value="[^abcd]">/gm | |
<button onclick="$('#input08').val('[^abcd]')">[^abcd]</button> | |
<button onclick="$('#input08').val('[a^bcd]')">[a^bcd]</button> | |
<button onclick="$('#input08').val('[^a-z]')">[^a-z]</button> | |
<button onclick="$('#input08').val('[^a-]')">[^a-]</button> | |
<button onclick="$('#input08').val('[^]')">[^]</button> | |
<button onclick="$('#input08').val('.')">.</button></p> | |
<p><input type="button" value="チェックする" onclick="check('08', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out08"></pre> | |
<p>「[^abcd]」という文字クラスは、「a、b、c、d以外のいずれか1文字」にマッチします。^は、直後の文字ではなく、文字クラス全体の意味を反転させます。</p> | |
<p>^は、[の直後に置かれた場合にだけ、文字クラスの意味を反転させます。[a^bcd]と書いた場合、^はリテラルとして扱われます。また、[^abc]だけでなく、[^a-z]のように-の範囲指定全体を反転させることもできます。[^a-d123]</p> | |
<p>文字クラスの中の^が[の直後にだけメタ文字になるのと同様、文字クラスの中の-は、前後に文字がある場合にのみ、範囲指定の意味を持ちます。-の後に文字が続かない場合は、リテラルの-になります。</p> | |
<p>「[^]」は、変則的ですが、「否定対象文字が無い→全ての文字にマッチする」となります。だたし、全ての文字にマッチさせるなら、正規表現メタ文字の.(ドット)を使ったほうが、わかりやすいです。</p> | |
<p>一点、注意が必要なのは、以下のようなケースです(検索対象文字列は、「ゼロ文字」、何の文字も無い状態です)。</p> | |
<pre class="regsub" id="subject09"></pre> | |
<p>正規表現:/<input type="text" name="input" id="input09" value="[^a-zA-Z0-9]">/gm | |
<button onclick="$('#input09').val('[^a-zA-Z0-9]')">[^a-zA-Z0-9]</button> | |
<button onclick="$('#input09').val('^$')">^$</button></p> | |
<p><input type="button" value="チェックする" onclick="check('09', 'gm')"></p> | |
<p>結果:</p> | |
<pre id="out09"></pre> | |
<p>この場合、否定文字クラスにどんなパターンを用いても、マッチすることはありません。否定文字クラスは、指定した範囲以外のいずれか1文字にマッチする、という意味なので、1文字もない場合にはマッチしないのです。</p> | |
<p>しかし、「^$」という正規表現であれば、「1文字も無い行」にマッチさせることができます。</p> | |
<h3 class="title-h3">正規表現のデリミタ</h3> | |
<p>ここまで、以下のようなインプットボックスを使ってきました。</p> | |
<p>正規表現:/<input type="text" name="input" id="" value="[a-z]">/gm</p> | |
<p>インプットボックスの前後にある/は、は、正規表現の始まりと終わりを示すのに一般的に使われる記号で、「デリミタ(delimiter)」と呼ばれます。delimitは「範囲を定める」という意味で、/〜/までの間が正規表現である、という「範囲を定め」ています。JavaScriptであれば、正規表現は以下のように使います。</p> | |
<p><code>"abc123".match(/[a-z]/)</code> <button onclick='$("#out").text("abc123".match(/[a-z]/))'>実行</button></p> | |
<p><span id="out"></span></p> | |
<p>このJavaScriptは、"abc123"という文字列の中から、[a-z]の範囲の文字を見つけてきて、一番最初にマッチした文字を返します(この場合、「a」)。</p> | |
<p>デリミタは、プログラム中で正規表現を使う際は必要なことが多く、エディタでは不要なこともあります。また、/〜/を使うことが一般的ですが、他の半角記号を使えることもあります。</p> | |
<h3 class="title-h3">正規表現の実行モード</h3> | |
<p>もう一度、以下のインプットボックスを見てみましょう。</p> | |
<p>正規表現:/<input type="text" name="input" id="" value="[a-z]">/gm</p> | |
<p>デリミタの後ろに、「gm」という文字が付いています。これは、正規表現の実行モードを制御する文字です。「g」は、globalのこと。動かしてみれば分かりやすいので、次で試してみましょう。</p> | |
<pre class="regsub" id="subject10"> | |
12345abcde | |
</pre> | |
<p>正規表現:/<input type="text" name="input" id="input10" value="[a-z]">/ <input type="button" value="チェックする" onclick="check('10', '')"></p> | |
<p>結果:</p> | |
<pre id="out10"></pre> | |
<pre class="regsub" id="subject11" style="display:none;"> | |
12345abcde | |
</pre> | |
<p>正規表現:/<input type="text" name="input" id="input11" value="[a-z]">/g <input type="button" value="チェックする" onclick="check('11', 'g')"></p> | |
<p>結果:</p> | |
<pre id="out11"></pre> | |
<p>gフラグを付けない状態では、最初にマッチする文字を見つけた段階で処理が止まります。しかし、gフラグを付けて正規表現を実行すると、一度マッチしても止まらず、検索対象文字列の末尾まで、繰り返しマッチします。</p> | |
<p>この記事では、ここまで、マッチした文字の範囲が分かりやすいように、常にgフラグを付けて正規表現を実行してきました。しかし、文字クラスのマッチ対象は、あくまで「1文字」です。[a-z]という正規表現は、[a-z]という範囲の文字全てにマッチするのではなく、[a-z]の範囲の1文字にマッチします。</p> | |
<p>次に、mフラグについて見ます。mフラグは、^と$の挙動を制御するフラグです。</p> | |
<pre class="regsub" id="subject12"> | |
abc | |
ABC</pre> | |
<p>正規表現:/<input type="text" name="input" id="input12" value="[a-zA-Z]$">/ <input type="button" value="チェックする" onclick="check('12', '')"></p> | |
<p>結果:</p> | |
<pre id="out12"></pre> | |
<pre class="regsub" id="subject13" style="display:none;"> | |
abc | |
ABC</pre> | |
<p>正規表現:/<input type="text" name="input" id="input13" value="[a-zA-Z]$">/m <input type="button" value="チェックする" onclick="check('13', 'm')"></p> | |
<p>結果:</p> | |
<pre id="out13"></pre> | |
<p>mフラグがない場合、検索対象文字列全体を1行とみなします。そのため、$は文字列全体の末尾にマッチします。mフラグを使うと、検索対象文字列に改行記号が含まれる場合、そこで改行されたと考えます。そのため、$は最初の改行も末尾にマッチします。</p> | |
<h3 class="title-h3">おわりに</h3> | |
<p>今回の記事を執筆するにあたって役に立ったリソースをまとめておきます。</p> | |
<p><a href="http://regexpal.com/">Regex Tester</a>は、オンラインの正規表現テストツールです。JavaScriptに対応しています。作者のSteven Levithan氏は、『<a href="http://www.oreilly.co.jp/books/9784873114507/">正規表現クックブック</a>』の著者でもあります。</p> | |
<p>JavaScriptの正規表現については、<a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp">Mozillaのリファレンス</a>が充実しています。利用可能な特殊文字(メタ文字)のリストもあり、重宝します。</p> | |
<script> | |
function check(id, flag) { | |
var subject = document.getElementById("subject" + id).innerHTML; | |
var input = document.getElementById("input" + id).value; | |
var pattern = new RegExp("(" + input + ")", flag); | |
var result = subject.match(pattern); | |
if (null === result) { | |
var newstr = "マッチしませんでした…"; | |
} else { | |
var result = subject.replace(pattern, "<span style='color:red;'>$1</span>"); | |
if (null === flag) { | |
var newstr = "マッチしました! " + result; | |
} else { | |
var newstr = "マッチしました!\n" + result; | |
} | |
} | |
document.getElementById("out" + id).innerHTML = newstr; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment