
JavaScriptで文字列検索するには?正規表現を使った方法も紹介
目次
JavaScriptの文字列検索とは?
特定の単語がテキスト中に存在するか調べる「検索」という行為は、日常的にもよく行うことです。例えば、専門書を読む際には巻末の索引から知りたい知識に関連する単語を探します。もっと一般的には、宝くじの当せん番号を自分の手持ちの番号と見比べて一致しているか確認する行為も検索に該当します。
コンピューターが扱うデータ化されたテキスト内から目的の一部分を探す、というのは主にコンピュータープログラムが行うもので、HTMLというテキストを扱うことに長けているJavaScriptにも文字列を検索する機能が備わっています。この記事では、JavaScriptの文字列の検索機能について説明します。
JavaScriptの文字列の検索方法
文字列を検索する、という行為でもっとも単純なものはある文字列ともう一つの別の文字列が完全に一致しているか比較するというものです。検索の例として先ほど宝くじを挙げましたが、ここで試してみましょう。
1 |
const FIRST_PRIZE_LOT = ""89組23450123"";let myLot = ""89組23450123"";if(myLot === FIRST_PRIZE_LOT) { console.log(""一等が当たりました!"");} |
自分のくじの番号myLot
が一等の番号FIRST_PRIZE_LOT
とまったく同じであるかを比較演算子===
を用いてチェックしています。比較演算子を用いて文字列が完全一致しているかをテストできます。これも検索の一種ではありますが、普通はもっと長い文字列の中から目的の一部分を探すケースが多いです。以降は、そのような場合に利用できるJavaScriptのStringオブジェクトのメソッドをご紹介していきます。
String.prototype.startsWith()
String.prototype.startsWith()
メソッドは文字列が指定された文字列から始まっているかをテストします。指定された文字列から始まっていた場合は結果がtrue
となり、始まっていない場合はfalse
になります。
1 |
const TARGET_URL = ""https://example.com/"";if(TARGET_URL.startsWith(""https:"")) { console.log(""SSLにより暗号化されています。"");} else { console.log(""暗号化されていません。"");} |
上記の例のように、URL文字列などで先頭に特定の文字列が存在するのかを判定するために利用できます。
String.prototype.endsWith()
String.prototype.endsWith()
メソッドは、文字列が指定された文字列で終わっているかをテストします。指定された文字列で終わっていた場合は結果がtrue
となり、そうでない場合はfalse
となります。
1 |
const THIRD_PRIZE_LOT = ""6543"";myLot = ""34組09876543"";if(myLot.endsWith(THIRD_PRIZE_LOT)) { console.log(""下4桁が一致しました。三等です!"");} else { console.log(""はずれです。"");} |
イメージしやすいように宝くじの下4桁の当せん番号を例に示しましたが、他にもテキストデータの終端記号のチェックなどに利用できます。
String.prototype.includes()
String.prototype.includes()
メソッドは指定された文字列が含まれているかをテストします。先頭や末尾といった箇所に関わらず、検索対象の文字列のどこかに指定の文字列があれば結果はtrue
になり、どこにも指定の文字列が無ければfalse
になります。
1 |
const FAVORITES = ""ジョギング、野球、カレーライス、映画鑑賞、緑茶"";if(FAVORITES.includes(""カレーライス"")) { console.log(""あなたはカレーライスが好きですね?"");} |
indexOf()とlastIndexOf()
ここまでご紹介したメソッドは、どれも結果を真偽値で返すものでした。includes()
メソッドは特定の文字列が含まれていることが判定できますが、文字列中のどこに含まれているのかまではわかりません。指定した文字列が出現する位置を調べたい場合は、String.prototype.indexOf()
メソッドかString.prototype.lastIndexOf()
メソッドを使います。indexOf()
は指定した文字列が最初に出現する位置を返します。
1 |
let index = ""赤巻紙青巻紙黄巻紙"".indexOf(""巻紙"");console.log(index); // 1が表示される。 |
対して、lastIndexOf()
は指定した文字列が最後に出現する位置を返します。
1 |
let index = ""赤巻紙青巻紙黄巻紙"".lastIndexOf(""巻紙"");console.log(index); // 7が表示される。 |
文字列の位置は0から始まります。上記の2つの例では、indexOf()
は最初の""赤巻紙""
の部分の""巻紙""
が検索され、それが文字列中の2文字目から始まっているので位置は1
となります。lastIndexOf()
では、最後の""黄巻紙""
の""巻紙""
が検索され、8文字目の位置7
が返されます。もし指定した文字列が見つからなかった場合には、どちらのメソッドも-1
を返します。
1 |
if(""赤巻紙青巻紙黄巻紙"".indexOf(""緑巻紙"") < 0) { console.log(""見つかりませんでした。"");} |
正規表現を利用した高度な検索方法
Stringオブジェクトが提供しているinclude()
やindexOf()
などのメソッドは、いずれも引数に文字列を指定し、指定した文字列とまったく同じ部分があるか、という判定を行うことはできました。しかし、単語には””卵””や””玉子””などのように複数の表記をするものがあり、それらすべてを検索したい場合もあります。また、電話番号のように形式が決まっていて、その形式に一致する部分を判断したい場合もあります。そのような場合には、検索対象の文字列を「パターン」としてまとめて、そのパターンに合致するかどうかを判定します。
文字列のパターンを表現する記法に正規表現という記述方法があり、JavaScriptにも正規表現による文字列検索機能が備わっています。正規表現にはとても複雑なパターンも表現できるほどの機能があり、それらをすべて網羅し解説するためには、書籍にまとめるほどの情報量が必要です。ここでは、JavaScriptにおける正規表現の便利な機能と、簡単なパターンの説明をすることにとどめます。
パターンマッチによる判定
例えば、利用者からのコメントを受け取り、表示するWebサービスを作成していることにします。その場合に電話番号などの個人情報が表示されるのは問題があるため、電話番号が含まれているか事前に検出したいとします。そのようなパターンを検出するのには、RegExp.prototype.test()
メソッドが利用できます。
ここでは””123-456-7890″”のように「数字3桁-数字3桁-数字4桁」のものを電話番号である、と判定することにします。JavaScriptの正規表現は/パターン/
というように、スラッシュ記号/
でパターンを表現する文字列を囲みます。正規表現を使うと、先ほどの電話番号のパターンを検索する処理は以下のようになります。
1 |
let comment = ""こんにちは!785-009-1586連絡まっています。"";let pattern = /\d{3}-\d{3}-\d{4}/;if(pattern.test(comment)) { console.log(""個人情報が含まれています。"");} |
変数pattern
に正規表現を代入しています。正規表現では、\d
は0から9の数字1文字を表します。{3}
は直前の表現の3回の繰り返しを表します。ハイフン記号-
は上記の例ではそのままのハイフン1文字、という意味です。つまり、pattern
に代入された/\d{3}-\d{3}-\d{4}/
という正規表現は「数字3回-数字3回-数字4回」という意味になります。スラッシュ記号/
で囲まれた部分はRegExp
オブジェクトとなり、RegExp
オブジェクトが持つメソッドが利用できます。上記の例ではRegExp
オブジェクトのtest()
メソッドにcomment
を渡して実行しています。
test()
メソッドは引数に渡された文字列の中に、パターンに一致する部分があるかどうかを判定します。一致する部分がある場合はtrue
が返され、一致しない場合にはfalse
が返されます。このように、正規表現を使用することで文字列の中からパターンに一致する部分を検索できます。
マッチした部分を取得する
文字列の中に特定のパターンが出現するかどうかをRegExp.prototype.test()
メソッドで判定できました。続いてはただ判定するだけではなく、パターンに一致する部分を取り出して、何かしらの処理ができるようにしたいと思います。パターンに一致する部分を取得するには、String.prototype.matchAll()
メソッドが利用できます。
文章からカタカナの単語を抜き出す処理を考えてみます。JavaScriptの正規表現は、文字をUnicodeのコード値に基づいて検索することが可能で、カタカナはUnicode上で範囲が決まっています。つまりUnicodeの特定の範囲にある文字の連続からなる文字列をパターンで表現すればよいということになります。
1 |
let sentence = ""昨日の夕飯はビーフシチューでデザートはヴァニラアイスでした。"";let pattern = /[\u30a1-\u30f4\u30fc]+/g;for(let result of sentence.matchAll(pattern)) { console.log(result[0]); // ビーフシチュー デザート ヴァニラアイスと表示される。} |
正規表現のパターンは/[\u30a1-\u30f4\u30fc]+/g
です。正規表現中の角括弧[]
で囲まれた部分には、一致させたい文字の候補を列挙できます。カタカナ1文字を一致させたいので、[アイウエオ...]
というようにカタカナをすべて書くこともできますが、それではプログラムが少々長くなってしまいます。そういった場合、候補の文字が文字コード上で連続しているのであればハイフン-
で繋げて間を省略できます。そこでUnicodeのカタカナの範囲で小文字の「ァ」から「ヴ」に該当するコード値の範囲を\u30a1-\u30f4
と表現しています。
続いての\u30fc
は長音を表す「ー」です。Unicode上では「ヴ」から「-」の間に「ヷ」などのあまり使わない文字があるので、このケースではカタカナであると見なさずに除外しています。例ではUnicode値を使って連続したコード範囲であることを強調した書き方にしていますが、コードの読みやすさを重視する場合は[ァ-ヴー]
と書くこともできます。角括弧の後のプラス記号+
は、直前の表現の1回以上の繰り返しを意味します。
角括弧内はカタカナとみなす文字1文字を表しますので、+
をつけることで「カタカナ1文字以上からなる文字列」という表現になります。正規表現の最後にg
という文字がありますが、これはフラグというもので正規表現全体の挙動に影響を与える設定値のようなものです。フラグには他にも英語の大文字小文字の違いを無視するような設定をするものなどがあります。
フラグg
は主に検索対象全体をチェックする場合に指定します。この後で使用しているmatchAll()
メソッドを利用するためにはg
フラグを指定する必要があります。実際の検索処理にはString.prototype.matchAll()
メソッドを使用しています。matchAll()
は、引数に正規表現を受け取り、表現に一致するすべての部分文字列を取得できるオブジェクトを返します。
注意したいのが、戻り値は結果そのものではなく「結果を取得できるオブジェクト」ということです。戻り値はイテレーターという一致した文字列を順番に取得できる機能をもっているので、for...of
文でループさせて検索結果を取得しています。イテレーターから取得できる検索結果は配列になっており、その先頭に一致した文字列が格納されています。上記の例ではresult[0]
というように参照して結果を表示しています。
文字列の検索処理は漏れが無いように
JavaScriptの文字列検索について、目的の文字列そのものを見つける方法から、正規表現を使った方法まで紹介しました。特に正規表現は使いこなせれば非常に強力で便利です。複雑なパターンにも適用できますが、その分難しく間違いが起こりやすい記述方法です。パターンの記述ミスによって検索漏れが発生しないように、まずは検索処理をindexOf()
などの簡単な処理の組み合わせで実現できないか考え、それから正規表現の利用を検討してみてください。