正規表現:RegExp()
「正規表現」ってなんだ?
- そんなの知らないから今までは非正規な表現をしていたかも?なんて考えてしまいそうですが、そういう意味ではありません。元の英語は「Regular Expression」
- このRegularは”規則的な”とか”規則正しい”という意味ですから、”規則表記”と訳したほうが判り易いと思います。
いったい何の規則のこと?
- それは文字の並び方の規則です。
- 突然ですがここで問題です。次の単語の共通点はなんでしょう?
- [apple] [adobe]
- 「コンピュータ」とか「Mac」などを思い浮かべるでしょう。ブッブ~!
- 正解は「aという文字で始まってeで終わる5文字」です。
- 正規表現では単語の持つ意味は無視して、単純に文字の並びを扱います。
規則を簡潔な記号に置き換える
- 先ほどの規則は日本語での説明ですから、これをコンピュータが処理できる表現に変換すると正規表現が完成します。
- 「aという文字で始まってeで終わる5文字」を正規表現にすると
- a...e
- 日本語の長い説明がこんなに簡単になってしまいます。
- WindowsやUNIXのワイルドカード(a???e)に似ていますね。しかし正規表現はそれよりも簡潔かつ細かい指定が出来るのです。
その使い道は?
- 正規表現それだけでは何も出来ません。検索や置換メソッドの引数に使うことで実力を発揮します。
テキスト
- match(/正規表現/)
- replace(/正規表現/,”文字列”)
- split("正規表現”)
- search(/正規表現/)
正規表現のメリット
- 特定の文字列だけでなく、揺れ幅を持った表現ができる。
- テキスト処理が簡単になる。
- 大量の複雑な処理をすることが出来る。
なお、文字列が正規表現に適合することを”マッチする”と言います。by 近藤
正規表現の定義
- JavaScriptの正規表現はRegExpオブジェクトで表現されます。RegExp()コンストラクタで生成できますが、後述するリテラルを使って生成するのが簡単で、一般的です。
- 次のコードはカット番号(cut000~999)の正規表現定義の例です。
var pattern= / cut\d{3} / ;
- 正規表現(緑文字部分)の前後をスラッシュ / /で囲む。これだけで正規表現を定義したことになります。
- 次のように定義することも出来ます。
var pattern= new RegExp(" cut\d{3} ") ;
- この場合は引用符 " " で囲みます。
メタ文字
正規表現に使う文字には2種類あります。
リテラル文字とメタ文字
- リテラル文字は文字そのままの意味で、例えば「¥」は円を表しています。
- メタ文字は文字本来の意味ではなく、パターンを記述するために特別な意味を持たせた文字です。
- 上の例では\dがメタ文字です。\とdの2文字で1つのメタ文字で、これで「数字1文字」を表しています。数字の0から9までのどれか1文字を意味します。\d{3}で数字3文字を意味します。
- 1文字だけのメタ文字もあります。例えば[.]ピリオドです。
- a.
- これはaの次にピリオドが続くのではなくて、任意の1文字が続くことを意味します。つまり[ab][af][a<] などを意味し、文字以外の記号も含まれます。
- 冒頭の答え「a...e 」の"a"と"e"がリテラル文字で、中間の"."(ピリオド)3つがメタ文字です。
- ではリテラル文字のピリオドはどう表すのか?
- それにはピリオドの前に\を付けます。
- \.
- この2文字でピリオド1つの意味になります。
- 例:ファイル名「a001.tga」→ [ a\d{3}\.tga ]
※重要:\記号表記についてこちらを確認しておいてください。エスケープ文字
主なメタ文字
. ピリオド | 任意の1文字 |
---|---|
[ ] | [ ]で囲んだ文字のどれかが一致 例:[ABC]はAかBかCのいずれか1文字 |
[^] | 否定。[ ]で囲んだ文字のどれも一致しない 例:[^ABC] はABC以外の文字に一致する |
- ハイフン | 文字の範囲を指定 例:[a-z]はアルファベット小文字のaからzまで |
\d | 数字1文字 [0-9]と同じです |
\D | 数字1文字以外 [^0-9]と同じです |
\w | アルファベットか数字1文字 [0-9a-zA-Z]と同じです |
\W | アルファベットでも数字でもない1文字 [^0-9a-zA-Z]と同じです |
* | 直前のパターンの0回以上の繰り返し 例:[.*]は任意の文字列ですから全ての文字列を表します 例:[cut0*\d]は [cut1]も[cut0001]もマッチします。 |
---|---|
+ | 直前のパターンを1回以上繰り返す |
? | 直前のパターンが0または1回 |
{n,m} | 直前のパターンがn回以上m回以下繰り返す |
{n,} | 直前のパターンをn回以上繰り返す |
{n} | 直前のパターンをn回繰り返す |
( ) | グルーピング。文字列や正規表現を量指定子を使って繰り返すときに使う。 |
---|---|
| | いずれかの正規表現に一致するという条件 例:(N|SH)906という正規表現は[N906]か[SH906]にマッチします。 |
\s | 空白文字 |
---|---|
\S | 空白以外の文字 |
\t | タブ |
\n | 改行 |
\r | リターン |
\f | 改ページ |
^ | 行頭 |
---|---|
$ | 行末 |
\b | 英単語の初めと終わり |
\ | エスケープ文字 |
オプション
- オプション(修飾子、フラッグ)が3つあります。
i | 大文字小文字を区別しない |
---|---|
g | 一致するもの全てをマッチング |
gi | 大文字小文字の区別無く全てをマッチング |
- 最初一致優先
- 文字列の中にマッチする文字列が複数あった場合にフラッグを付けないと、一番最初に一致するものだけがマッチします。
- [g]を付けると全てに対応します。
- 下は置換の例です。
- 元の文字列[AfterEffects]には[e]が2個含まれています。
var txt="AfterEffects";
txt.replace(/e/,"#");
結果:Aft#rEffects
- オプションをつけないと最初の[e]だけがマッチ。
txt.replace(/e/g,"#");
結果:Aft#rEff#cts
- [g]をつけると全ての[e]にマッチ。
txt.replace(/e/gi,"#");
結果:Aft#r#ff#cts
[g][i]両方つけると大文字のEもマッチ。
具体例
- 某スタジオのコンポジション名はこのようになっています。
SB_07_125b_T2
- このコンポ名はあるルールに則って付けられてます。
- 規則:作品名+話数+カット番号+リテーク番号
- 其々の要素をアンダースコアーでつないでいるので、split("_")メソッドで各情報を取得出来るように考えられています。
正規表現を使ってカット番号を###に置き換えてみましょう。
- まず、このコンポ名に含まれるのカット番号の特徴を探します。
- カット番号は数字3文字。
- 稀に番号の後にアルファベットが1文字付く。
- 番号は100以下でも[005]のように全て3桁。
- これを正規表現にします。
- 数字1文字は [ \d ]
- それを3桁にする [\d{3}]
- 記述は[ \d\d\d ]でも可です。
- もう1つは最後にアルファベットが付く場合 [\d{3}[a-z]]
- zまでは無いと思いますが…
- 最後にこの2つのどちらかという条件[ | ]でつなぎます。
var compNa="SB_07_125b_T2";
var reg= /\d{3}|\d{3}[a-z]/ ;
compNa.replace(reg,"###");
結果:SB_07_###_T2
- ※話数は3桁にならないとします。
リテーク番号の扱い
- 次に、コンポ名からリテーク番号を取り出すケースを考えます。
- コードは次のようになります。
var compNa="SB_07_125b_T2";
var take=compNa.split("_")[3];
結果:T2
- しかしリテーク番号はTake1の時は記述しないほうが現実的です。
- そこでリテーク番号[_T1]を省略してコンポ名を「SB_07_125b」と記述すると、上のコードはエラーになります。(理由:配列の[3]が存在しないから)
- これに対応するにはリテーク記述の有無で処理を分ける必要があります。
- 判断の仕方はいくつか考えられますが、ここはでは正規表現を使ってリテーク記述の有無を判断してみましょう。
- リテーク部分の正規表現は[ _T ]+数字1文字ですから[ _T\d ]ですね。
var reg=/_T\d/;
var res=compNa.search(reg); //無ければ[ -1]を返す
if(res<0){処理A} //リテーク無し
else{処理B} //リテーク有り
- もしリテーク番号に[T]と[t]が混在するのなら、[i]フラッグを付けます。
reg=/_T\d/i;
- これで[T3]でも[t3]でも拾うことができます。
- リテークが2桁になった場合(例:T12)でもマッチするので( _T12)このまま使うことが出来ます。(そんなリテークやめてくれ!)
- [T]の前の[_]アンダースコアーは無くてもよさそうですが、作品タイトルが[T3]とか[CAT2]だと、マッチしてしまう可能性があるので[ _T]としています。
- 1行目と2行目は次のようにまとめることができます。
var res=compNa.search(/_T\d/);
- 番号を見て何の機種だか判りますか?
N902iS SH703i P906i
- これはDocomo携帯のかつての機種名です。これには次のような規則性がありました。
- 規則:メーカー名+シリーズ番号+i(Mode)
この機種名の規則を正規表現にしてみましょう。
- 上で見つけた規則はメーカー名やシリーズ番号など人が理解しやすい規則です。それを純粋に文字の並びの規則として見直します。すると…
- アルファベット大文字で(NかSHかP)
- 次に数字3文字
- そしてアルファベットの(iかiS)
- これを正規表現で記述するとこうなります。
(N|SH|P)\d{3}(i|iS)
- 別のパターンで表記することも出来ます。例えば
- メーカー名の部分はアルファベット1文字又は2文字、という表現も出来ます。
- [A-Z]{1,2}
- その場合は[ SO907i ]という名前もマッチします。
- 数字の部分は9か7、次に0ゼロ、そして(2か3か6)という見方も出来ます。
- (9|7)0(2|3|6)
- その場合[ N711i ]という名前は除外されることになります。
- このようにどんな文字列を対象にするのか、出来るだけ多くのサンプルにあたって検討しないと思わぬバグを残してしまうことになります。
正規表現の手順 まとめ
- 1,対象になる文字列の例を全て列挙する。
- 2,パターンの検出。
- 3,正規表現にする。
- 正規表現は非常に奥が深いので、詳しく知りたい方はググッたり関係本を読んだりしてください。
- JavaScriptの正規表現はPerl言語のそれとほとんど同じですので、Perlの正規表現を勉強するのもいいと思います。
- 他人任せで済みません m(_ _;)m