JavaScript 検索文字列を全て置換する【replaceAll, split join】

replaceは最初の検索文字しか置き換わらない

replaceメソッドを用いた標準の方法では、一番最初にマッチした文字列のみが置き換えられます。replaceFirst的な挙動ですね。

"a b c".replace(' ', '-') // "a-b c"

JavaScriptで文字列を全置換する方法

JavaScriptで文字列を全置換するには、正規表現を用いるか、split join メソッドを組み合わせるテクニックを用いる必要があります。

"a b c".replace(/ /g, '-')   // "a-b-c"
"a b c".split(' ').join('-') // "a-b-c"

前者がreplaceAll処理に相当する「# 正規表現で文字列を全て置き換える」方法です。
後者は「# split と join で文字列をすべて置換する」テクニックです。

より確実でオススメな方法は後者の split join を用いた置き換え方法ですが、置換処理であることを明確に示したい場合には、replaceメソッドと正規表現を用いた前者の方法を用いることもできます。利用の際の注意点や、両処理の仕組みについては次項の解説を参考にしてください。

目次

replaceAllメソッドは使えない

JavaScriptの文字列にはreplaceAllメソッドが存在しないため、replaceAllを用いるとエラーが発生してしまいます。

"a".replaceAll('a', 'b') // Uncaught TypeError: "a".replaceAll is not a function

そのため、特定の文字列を全て置換したい場合には、replaceメソッドに正規表現を用いるか、split joinのイディオムを用いる必要があります。

正規表現で文字列を全て置き換える

一致したテキスト全てを置き換えたい場合は、正規表現を用いる必要があります。

"a b c".replace(/ /g, '-') // "a-b-c"

gでグローバルマッチを行っている所が肝です。これで一致した全ての文字が置換えの対象になります。replaceAllやgsub的な置換えが実現されるわけですね。JavaScriptにはreplaceAllメソッドがないためこのようなテクニックが必要となります。

正規表現の記法に注意

正規表現を用いるの際には、.*等の特殊な文字を置き換える際にバックスラッシュ/文字によるエスケープ処理が必要になるので注意してください。

"a.b.c".replace(/\./g, '-') // "a-b-c"

JavaScriptではおよそ「\^$*+?.()|{}[]-/」の各記号が正規表現の特殊文字として解釈されるため、エスケープが必要となります。

空文字による置換を実現する場合

空文字による置き換えを行う場合には、正規表現リテラルは利用できないため、代わりにRegExpオブジェクトを用いる必要があります。

"ab".replace(new RegExp("", 'g'), "-") // "-a-b-"
"ab".replace(//g, "-") // Uncaught SyntaxError: Unexpected end of input

split と join で文字列をすべて置換する

splitメソッドとjoinメソッドを組み合わせることで、文字列要素の全置換を実現することができます。

"a b c".split(' ').join('-'); // "a-b-c"

splitは文字列の分割結果を配列形式で返すメソッドです。そしてその結果をjoinメソッドで結合することで、文字列への再変換を実現します。

"a b c".split(' ')        // ["a", "b", "c"]
["a", "b", "c"].join('-') // "a-b-c"

これによって分割対象の文字列要素が取り除かれることになるため、事実上の全置換を実現することができます。

split join の処理コストについて

なお、split join イディオムによる処理コストはそれほど大きくならないのが一般的です。ブラウザ側の最適化やコピーオンライトを前提とする参照の仕組みが上手く働く可能性もあります。バッファーの書き換えとコピーが頻繁に発生するreplaceメソッドよりも高速で効率的な置換が期待できます。またその差は置き換え対象の要素が多くなればなるほど広くなると考えられます。

replace と split join のどちらを使うべきか

安全性の観点ではsplit joinイディオムを用いるのがオススメです。正規表現を用いた方法では書き手のミスによる意図しないマッチングが発生してしまう恐れがあるためです。動的な置換文字列を指定する際には自前でのエスケープ処理が必須となります(なおRegExp.escapeは現在非対応)。

処理速度については、replaceメソッドや正規表現を用いた方法よりも、split joinイディオムを用いた方法のほうが高速に動作する場合があります。「# split join の処理コストについて」も参考にしてください。

replace_all 関数を実装する方法

純粋な全置換を実現する関数を独自に定義することも可能です。以下は文字列検索と文字列連結を活用した置き換えロジックです。原始的な処理ではありますが、replaceメソッドやsplit joinイディオムを用いた方法よりも高速に動作するという特徴があります。

function replace_all(string, target, replacement) {
  var result = "";
  var offset = 0;
  var target_length = target.length;
  if (target_length === 0) {
    for (var i = 0, c = string.length; i < c; i++) {
      result += string[i];
      result += replacement;
    }
    if (result.length)
      return result.substr(0, result.length - replacement.length);
    return result;
  }
  do {
    var i = string.indexOf(target, offset);
    if (i === -1) {
      result += string.substring(offset);
      return result;
    }
    result += string.substring(offset, i);
    result += replacement;
    offset = i + target_length;
  } while (true);
}
広告