JavaScript string replace完全ガイド【基本から正規表現まで】

はい、承知いたしました。JavaScriptのString.prototype.replace()メソッドに関する詳細な解説記事を記述します。約5000語で、基本から正規表現、応用までを網羅する内容となります。


JavaScript文字列置換の決定版!replace() メソッド完全ガイド【基本から正規表現・関数まで】

JavaScriptでの文字列操作は、Web開発において非常に頻繁に行われる処理の一つです。ユーザーからの入力の整形、データの変換、特定のパターンの抽出や削除など、その用途は多岐にわたります。中でも、文字列中の特定の箇所を別の文字列に置き換える「置換」処理は、文字通り操作の核となる機能と言えるでしょう。

JavaScriptで文字列の置換を行う際に、最も中心的な役割を果たすのが String.prototype.replace() メソッドです。このメソッドは、シンプルな文字列置換から、複雑なパターンマッチングと動的な置換まで、幅広いニーズに対応できる非常に強力なツールです。

しかし、その多機能さゆえに、使い方によっては「思った通りに置換できない」「なぜか最初の1つしか置き換わらない」といった疑問に直面することも少なくありません。特に、正規表現や置換関数といった高度な機能を使う際には、その仕組みを深く理解しておくことが重要です。

この記事では、JavaScriptの replace() メソッドについて、その基本的な使い方から、正規表現を使ったパターンマッチング、そして置換関数による高度な操作まで、網羅的に解説します。この記事を読めば、replace() メソッドを自信を持って使いこなし、JavaScriptでの文字列操作の幅が大きく広がるはずです。

さあ、replace() メソッドの世界へ飛び込んでいきましょう!

1. replace() メソッドとは? – 基本の理解

まずは、replace() メソッドの基本的な定義と構文から見ていきましょう。

replace() メソッドは、指定されたパターン(文字列または正規表現)にマッチした文字列の一部、または全てを、指定された置換文字列(文字列または関数)で置き換えた新しい文字列を返します。

重要: replace() メソッドは、元の文字列自体を変更するわけではありません(非破壊的メソッド)。常に新しい文字列を生成して返します。元の文字列を変更したい場合は、replace() の結果を元の変数に再代入する必要があります。

構文:

javascript
string.replace(searchValue, newValue)

  • string: 置換操作を行う元の文字列。
  • searchValue: 検索対象となるパターン。以下のいずれかを指定します。
    • String: 検索したい文字列リテラルを指定します。
    • RegExp: 検索したいパターンを表す正規表現を指定します。
  • newValue: searchValue にマッチした部分と置き換える内容。以下のいずれかを指定します。
    • String: 置換後の文字列リテラルを指定します。
    • Function: マッチした部分に応じて動的に置換文字列を生成するための関数を指定します。

戻り値:

置換操作が実行された新しい文字列が返されます。searchValue にマッチする箇所が見つからなかった場合は、元の文字列がそのまま返されます。

2. searchValue が文字列の場合 – シンプルな置換

最も基本的な使い方は、searchValue に文字列リテラルを指定する方法です。これは、特定の固定された文字列を別の文字列に置き換えたい場合に使用します。

“`javascript
const text = “Hello, world! Hello, JavaScript!”;

// “world” を “everyone” に置き換える
const newText = text.replace(“world”, “everyone”);

console.log(newText); // 出力: “Hello, everyone! Hello, JavaScript!”
console.log(text); // 出力: “Hello, world! Hello, JavaScript!” (元の文字列は変わらない)
“`

この例から分かるように、replace() は新しい文字列を返します。また、最も重要な点として、searchValue に文字列を指定した場合、最初に見つかったマッチのみが置換されます

“`javascript
const text = “Hello, world! Hello, JavaScript! Hello, again!”;

// “Hello” を “Hi” に置き換える (最初の一つだけ)
const newText = text.replace(“Hello”, “Hi”);

console.log(newText); // 出力: “Hi, world! Hello, JavaScript! Hello, again!”
“`

もし文字列検索で全てのマッチを置換したい場合は、後述する正規表現を使う必要があります。

文字列検索におけるもう一つの注意点は、大文字小文字を区別するということです。

“`javascript
const text = “apple Banana cherry”;

// “apple” を “APL” に置き換える (マッチする)
const newText1 = text.replace(“apple”, “APL”);
console.log(newText1); // 出力: “APL Banana cherry”

// “Apple” を “APL” に置き換える (マッチしない)
const newText2 = text.replace(“Apple”, “APL”);
console.log(newText2); // 出力: “apple Banana cherry” (元の文字列そのまま)
“`

大文字小文字を区別せずに置換したい場合も、正規表現の機能が必要になります。

文字列検索による置換は、対象が固定されており、かつ最初の一つだけ置換すれば良いというシンプルなケースに最適です。

3. searchValue が正規表現の場合 – パターンマッチングと複数置換

replace() メソッドの真価を発揮するのは、searchValue に正規表現を指定する場合です。正規表現を使うことで、単なる固定文字列ではなく、特定の「パターン」にマッチする文字列を検索・置換できるようになります。これにより、より柔軟で複雑な文字列操作が可能になります。

正規表現リテラル (/pattern/flags) または RegExp オブジェクトを使用できます。通常は正規表現リテラルが簡潔でよく使われます。

“`javascript
// 正規表現リテラル
const regex = /hello/gi;

// RegExp コンストラクタ
const regexObj = new RegExp(“hello”, “gi”);
“`

正規表現で replace() を使う最大の利点は、フラグを指定できることです。特に重要なのが以下のフラグです。

  • g (global): 文字列中の全てのマッチを検索し、置換します。これが、文字列検索の場合と最も大きく異なる点です。
  • i (ignore case): 大文字小文字を区別せずにマッチングを行います。

その他のフラグもありますが、まずは gi を理解することが重要です。

3.1. g フラグによる複数置換

searchValue に正規表現を指定し、g フラグを付けることで、文字列中の全てのマッチを一度に置換できます。

“`javascript
const text = “apple, banana, Apple, cherry, APPLE”;

// 大文字小文字を区別して、全ての “apple” を “fruit” に置換
const newText1 = text.replace(/apple/g, “fruit”);
console.log(newText1); // 出力: “fruit, banana, Apple, cherry, APPLE”

// g フラグがない場合 (最初の1つだけ置換)
const newText2 = text.replace(/apple/, “fruit”);
console.log(newText2); // 出力: “fruit, banana, Apple, cherry, APPLE” (あれ?これも同じ出力に…)
// 補足: ここは説明を修正します。gフラグがない正規表現も、replace()の動作としては最初のマッチのみを置換します。
// したがって、意図としては /apple/g と /apple/i を組み合わせる例を示すべきでした。

// — 正しい説明に修正 —

const textMultiple = “apple, banana, Apple, cherry, APPLE”;

// 正規表現で ‘g’ フラグがない場合 -> 文字列検索と同じく最初のマッチのみ置換
const resultNoG = textMultiple.replace(/apple/, “FRUIT”);
console.log(“gフラグなし:”, resultNoG); // 出力: “gフラグなし: FRUIT, banana, Apple, cherry, APPLE”

// 正規表現で ‘g’ フラグがある場合 -> 全てのマッチを置換
const resultWithG = textMultiple.replace(/apple/g, “FRUIT”);
console.log(“gフラグあり:”, resultWithG); // 出力: “gフラグあり: FRUIT, banana, Apple, cherry, APPLE” (やはりApple, APPLEは置換されていない)

// 次は ‘i’ フラグと組み合わせた例に進みます。
“`

g フラグは、対象のパターンが複数箇所に出現する場合に非常に役立ちます。例えば、HTML文字列から特定のタグを全て削除したり、文中の特定の単語を全て別の単語に置き換えたりする場合などです。

3.2. i フラグによる大文字小文字を区別しない置換

i フラグを使うと、大文字小文字を区別せずにパターンにマッチさせることができます。

“`javascript
const text = “apple, banana, Apple, cherry, APPLE”;

// 大文字小文字を区別せずに、全ての “apple” (Apple, APPLEも含む) を “FRUIT” に置換
const newText = text.replace(/apple/gi, “FRUIT”);
console.log(newText); // 出力: “FRUIT, banana, FRUIT, cherry, FRUIT”
“`

g フラグと i フラグは組み合わせてよく使用されます (gi フラグ)。これにより、「文字列中の全てのマッチを、大文字小文字を区別せずに置換する」という強力な操作が可能になります。

3.3. その他の正規表現フラグ

replace() で使用できるその他の便利なフラグも簡単に紹介します。

  • m (multiline): 正規表現の ^$ が行頭と行末にマッチするようになります。デフォルトでは文字列全体の先頭と末尾にマッチします。
  • s (dotall): 正規表現の . (任意の一文字) が改行文字 (\n, \r, \u2028, \u2029) にもマッチするようになります。デフォルトでは改行にはマッチしません。
  • u (unicode): Unicodeのサロゲートペアを含む文字を正しく扱えるようになります。絵文字などの2文字で表現される文字を正確にマッチさせたい場合に有用です。
  • y (sticky): パターンのマッチが、文字列の現在の位置 (lastIndex プロパティ) から開始されるようにします。g フラグと併用し、連続したマッチングを行う際に使用されます。

これらのフラグは、特定の高度なパターンマッチングで役立ちます。日常的な置換では gi が最も頻繁に使われるでしょう。

3.4. 正規表現を使ったより複雑な例

正規表現のメタ文字や量指定子などを組み合わせることで、非常に柔軟なパターンマッチングが可能になります。

例1: 数字を全てマスキングする

“`javascript
const text = “ユーザーID: 12345, 電話番号: 090-9876-5432”;

// 全ての数字を ‘‘ に置換
const maskedText = text.replace(/\d/g, “
“);
console.log(maskedText); // 出力: “ユーザーID: *, 電話番号: *”
“`

例2: HTMLタグを全て削除する

“`javascript
const html = “

これは太字のテキストです。

“;

// HTMLタグを全て空文字列に置換
const plainText = html.replace(/<\/?[^>]+(>|$)/g, “”);
console.log(plainText); // 出力: “これは太字のテキストです。”
“`

正規表現を使いこなすには別途学習が必要ですが、replace() と組み合わせることで、強力な文字列処理が可能になります。

4. newValue が文字列の場合 – 特殊な置換パターン

newValue に文字列を指定する場合、単なるリテラル文字列だけでなく、searchValue のマッチ結果を参照するための特別なパターン(ドル記号 $ で始まるシーケンス)を使用できます。これは、正規表現を使った置換で特に威力を発揮します。

以下の特殊文字シーケンスが使用可能です。

  • $$: リテラルのドル記号 $ を挿入します。
  • $&: マッチした文字列全体を挿入します。
  • $ `: マッチした部分より前の文字列を挿入します。
  • $': マッチした部分より後の文字列を挿入します。
  • $n または $nn: n 番目または nn 番目のキャプチャグループにマッチした文字列を挿入します。正規表現で括弧 () を使ってキャプチャグループを定義した場合に使用できます。

これらの特殊パターンを使うことで、マッチした内容をそのまま利用したり、文字列の特定部分を再配置したりといった操作が容易になります。

例1: マッチした単語を大文字に変換する (直接はできませんが、$& を使って検証)

“`javascript
const text = “hello world”;

// マッチした単語を全て探し、その後に同じ単語を挿入 (例として$&を使用)
const newText = text.replace(/(\w+)/g, “$& $&”);
console.log(newText); // 出力: “hello hello world world”
// 注: 直接大文字にするには後述の置換関数が必要です。
“`

例2: マッチした単語の前後に特定の文字列を追加する

“`javascript
const text = “apple, orange, grape”;

// 全ての単語の前に “-” を追加
const newText = text.replace(/(\w+)/g, “- $&”);
console.log(newText); // 出力: “- apple, – orange, – grape”
“`

例3: キャプチャグループを使って文字列の順序を入れ替える

これは非常に強力な使い方です。例えば、「姓, 名」という形式の文字列を「名 姓」に変換したい場合などに使えます。

“`javascript
const name = “Doe, John”;

// 正規表現で姓と名をキャプチャグループに分ける: /^(.+),\s(.+)$/
// グループ1: Doe ((.+))
// グループ2: John ((.+))
// $1 はグループ1、$2 はグループ2 にマッチした文字列を指す
const newName = name.replace(/^(.+),\s
(.+)$/, “$2 $1”);
console.log(newName); // 出力: “John Doe”

const date = “2023-10-26”;

// YYYY-MM-DD 形式を MM/DD/YYYY 形式に変換
// グループ1: 2023 (\d{4})
// グループ2: 10 (\d{2})
// グループ3: 26 (\d{2})
const formattedDate = date.replace(/^(\d{4})-(\d{2})-(\d{2})$/, “$2/$3/$1”);
console.log(formattedDate); // 出力: “10/26/2023”
“`

キャプチャグループ $n/$nn は、正規表現で括弧 () を使って部分パターンをキャプチャした場合に利用できます。$1 は最初の括弧、$2 は2番目の括弧、というように番号が割り当てられます。この機能を使うことで、マッチした文字列の構造を分解し、再構成するといった高度な置換が可能になります。

注意点: $nn は1から始まります。また、$0 は存在しません。$& がマッチした文字列全体に対応します。

5. newValue が関数の場合 – 動的な置換

replace() メソッドの最も強力で柔軟な機能は、newValue に関数を指定できることです。この関数は、searchValue にマッチが見つかるたびに実行され、その戻り値が置換後の文字列として使用されます。

置換関数は、マッチした文字列の内容に基づいて、動的に置換文字列を生成したい場合に非常に役立ちます。例えば、マッチした単語を条件付きで変換したり、数値をフォーマットしたり、特定の形式のデータを別の形式に変換したりといったことが可能になります。

置換関数は、通常以下の引数を受け取ります(引数の数は、正規表現にキャプチャグループが含まれるかどうかによって異なります)。

javascript
function(match, p1, p2, ..., offset, string) {
// 置換処理
// 戻り値が置換後の文字列になる
}

引数の詳細:

  1. match: searchValue(正規表現)全体にマッチした文字列。上記の `$&` に相当します。
  2. p1, p2, ...: searchValue(正規表現)内のキャプチャグループ () にマッチした文字列。正規表現にキャプチャグループが n 個含まれる場合、p1 から pn までの引数が渡されます。上記の $1, $2, … に相当します。
  3. offset: マッチが見つかった位置(インデックス)。元の文字列の先頭からのオフセット値です。
  4. string: 置換操作が行われている元の文字列全体。

置換関数は、searchValue に正規表現を指定し、かつ g フラグが付いている場合でも、マッチする箇所が複数あれば、それぞれのマッチに対して独立して実行されます。

置換関数の使用例:

例1: 各単語の先頭を大文字にする(タイトルケース)

“`javascript
const text = “hello world from javascript”;

// 各単語 (\w+) にマッチさせ、置換関数を呼び出す
const titleCaseText = text.replace(/\b\w/g, function(match) {
// マッチした文字 (単語の先頭文字) を大文字に変換して返す
return match.toUpperCase();
});

console.log(titleCaseText); // 出力: “Hello World From Javascript”

// より複雑な例: 全ての単語の先頭を大文字にする(空白区切り)
const textWords = “this is a test string”;
const resultTitleCase = textWords.replace(/(^|\s)\S/g, function(match) {
return match.toUpperCase();
});
console.log(“タイトルケース:”, resultTitleCase); // 出力: “This Is A Test String”
“`

例2: 数値にカンマ区切りを追加する

“`javascript
const price = “価格: 12345678円”;

// 3桁以上の数値にマッチさせ、置換関数でカンマを挿入
// 正規表現: (\d)(?=(\d{3})+$)
// (\d): 1桁の数字をキャプチャグループ1としてマッチ (カンマの左側の数字)
// (?=(\d{3})+$): 後方参照アサーション。マッチした数字の後に3桁の数字の繰り返しが続く(文字列末尾まで)ことを確認。これはマッチ自体には含まれない。
const formattedPrice = price.replace(/(\d)(?=(\d{3})+$)/g, function(match, p1) {
// match はマッチした数字 (\d)
// p1 はキャプチャグループ1にマッチした数字 (\d)
return p1 + “,”; // マッチした数字の後にカンマを追加
});
console.log(formattedPrice); // 出力: “価格: 12,345,678円”

const largeNumberText = “金額は1000000ドルです。”;
const formattedNumber = largeNumberText.replace(/(\d)(?=(\d{3})+(?!\d))/g, ‘$1,’); // 後方参照と特殊文字を使用
console.log(“カンマ区切り:”, formattedNumber); // 出力: “金額は1,000,000ドルです。”
``
(注: カンマ区切りの正規表現はいくつかパターンがあります。後方参照を使用する
$1,` のパターンも一般的です。)

例3: HTMLエンティティをデコードする

“`javascript
const encodedHtml = “これは <b>太字</b> です。”;

const entityMap = {
‘<‘: ‘<‘,
‘>’: ‘>’,
‘&’: ‘&’,
‘"’: ‘”‘,
‘'’: “‘”
};

// HTMLエンティティのパターンにマッチ (\&(?:lt|gt|amp|quot|#39);)
const decodedHtml = encodedHtml.replace(/<|>|&|"|'/g, function(match) {
// マッチしたエンティティ (match) を対応する文字に変換
return entityMap[match];
});

console.log(decodedHtml); // 出力: “これは 太字 です。”
“`

例4: 日付フォーマットの変換(YYYY/MM/DD to DD-MM-YYYY)

キャプチャグループを引数として受け取る例です。

“`javascript
const date = “2023/10/26”;

// YYYY/MM/DD パターンにマッチし、年、月、日をキャプチャグループに分ける
const newDate = date.replace(/^(\d{4})\/(\d{2})\/(\d{2})$/, function(match, year, month, day) {
// match: “2023/10/26” (全体)
// year: “2023” (グループ1)
// month: “10” (グループ2)
// day: “26” (グループ3)
return ${day}-${month}-${year}; // DD-MM-YYYY 形式で返す
});

console.log(newDate); // 出力: “26-10-2023”
“`

置換関数を使うことで、マッチした内容を基に、様々な条件分岐や追加処理を行ってから置換文字列を生成できます。これは、単に静的な文字列で置き換えるだけでは不可能な高度な文字列変換を実現する上で不可欠な機能です。

置換関数の引数の注意点:

  • 正規表現にキャプチャグループがない場合、関数は function(match, offset, string) のように3つの引数を受け取ります。
  • キャプチャグループがある場合、キャプチャグループの数に応じて p1, p2, ... の引数が追加されます。キャプチャグループの数は可変なので、関数定義ではレストパラメータ ...args を使うと、キャプチャグループ以降の引数(オフセット、元の文字列)もまとめて受け取れますが、通常は明示的に必要な引数(match, p1, p2, …)だけを定義することが多いです。

6. replace() の応用例と発展的な使い方

ここまで見てきた基本、正規表現、関数の組み合わせで、replace() は様々な場面で応用できます。

6.1. 複数パターンの置換

一度の replace() 呼び出しで、複数の異なるパターンを同時に置換したい場合があります。これは、正規表現の OR 条件 (|) を使うことで実現できます。

“`javascript
const text = “apple, orange, grape, banana”;

// “apple” または “banana” を “FRUIT” に置換
const newText = text.replace(/apple|banana/g, “FRUIT”);
console.log(newText); // 出力: “FRUIT, orange, grape, FRUIT”

// 複数の単語を置換しつつ、特定の単語だけ別のものに置換したい場合など
const words = “dog, cat, bird, fish”;
const replacedWords = words.replace(/dog|cat/g, “animal”).replace(/bird/g, “flyer”);
console.log(replacedWords); // 出力: “animal, animal, flyer, fish”
// このように、複数回 replace() をチェーンすることも可能です。
“`

正規表現の OR (|) は非常に便利ですが、複雑な正規表現になりすぎると可読性やパフォーマンスが低下する可能性もあります。その場合は、複数回 replace() を呼び出すか、置換関数内で条件分岐を行うなどの代替手段も検討できます。

6.2. 文字列のサニタイズ(無害化)/エスケープ

ユーザー入力をデータベースに保存したり、HTMLとして表示したりする前に、悪意のあるコード(XSSなど)を防ぐために特定の文字をエスケープすることがあります。

“`javascript
const userInput = ““;

// HTMLとして安全に表示できるように <, >, &, ” をエスケープする
const escapedInput = userInput.replace(/[<>&”]/g, function(match) {
switch (match) {
case ‘<‘: return ‘<‘;
case ‘>’: return ‘>’;
case ‘&’: return ‘&’;
case ‘”‘: return ‘"’;
default: return match; // ここに到達しないはず
}
});

console.log(escapedInput); // 出力: “<script>alert(‘XSS’)</script>”
“`

この例のように、置換関数内で switchif を使うことで、マッチした文字に応じて異なる置換文字列を返すことができます。

6.3. 特定の文字/パターンを削除する

newValue に空文字列 ('') を指定することで、マッチしたパターンを文字列から削除できます。

“`javascript
const phoneNumber = “090-1234-5678”;

// ハイフンを削除
const cleanNumber = phoneNumber.replace(/-/g, “”);
console.log(cleanNumber); // 出力: “09012345678”

const comment = “/ This is a comment / let x = 10;”;

// Cスタイルのコメントを削除 (単純化された例、ネストや複数行コメントは考慮していません)
const codeWithoutComment = comment.replace(/\/*.*?*\//g, “”);
console.log(codeWithoutComment); // 出力: ” let x = 10;”
“`

6.4. テンプレートエンジンのような簡易置換

特定のプレースホルダーを実際の値に置き換えるといった、簡易的なテンプレートエンジンのような使い方にも応用できます。

“`javascript
const template = “Hello, {name}! Your score is {score}.”;
const data = { name: “Alice”, score: 95 };

// {key} 形式のプレースホルダーを正規表現で探し、置換関数でデータから値を取得
const result = template.replace(/{(\w+)}/g, function(match, key) {
// match: “{name}” または “{score}”
// key: “name” または “score” (キャプチャグループ1)
return data[key] || match; // データにキーがあればその値を、なければ元のマッチを返す
});

console.log(result); // 出力: “Hello, Alice! Your score is 95.”
“`

この例では、キャプチャグループを使ってプレースホルダー内のキー名を取得し、そのキーを使ってデータオブジェクトから対応する値を取り出しています。

7. 注意点とパフォーマンス

replace() メソッドを使う上でいくつか注意すべき点があります。

7.1. 元の文字列は変更されない (Immutable)

これは既に述べましたが、非常に重要な点です。replace() は常に新しい文字列を返します。元の文字列を変更したい場合は、必ず戻り値を元の変数に再代入してください。

“`javascript
let text = “Old value”;
text.replace(“Old”, “New”); // この行だけでは text は変わらない
console.log(text); // 出力: “Old value”

text = text.replace(“Old”, “New”); // 戻り値を再代入
console.log(text); // 出力: “New value”
“`

7.2. searchValue が正規表現の場合のパフォーマンス

正規表現は非常に強力ですが、複雑すぎる正規表現や、非常に長い文字列に対して使用する場合、パフォーマンスに影響を与える可能性があります。

  • 非効率な正規表現 (Catastrophic Backtracking): バックトラッキングが大量に発生するような正規表現は、処理に非常に時間がかかることがあります。例えば、/(a+)+b/ のようなパターンは、特定の入力に対して非常に遅くなる可能性があります。正規表現の最適化は奥深い分野ですが、基本的な greedy/lazy マッチング (* vs *? など) や possessive 量指定子 (*+ など) を理解することが役立ちます。
  • RegExp オブジェクトの再利用: ループ内で同じ正規表現を繰り返し使用する場合、ループの外で一度 RegExp オブジェクトを作成し、それを使い回す方が効率的です。

“`javascript
// 非推奨 (ループごとに新しいRegExpオブジェクトを作成)
for (let i = 0; i < 1000; i++) {
“some string”.replace(/pattern/g, “replacement”);
}

// 推奨 (RegExpオブジェクトを再利用)
const pattern = /pattern/g;
for (let i = 0; i < 1000; i++) {
“some string”.replace(pattern, “replacement”);
}
“`
(ただし、近年のJavaScriptエンジンは最適化が進んでおり、単純なリテラルの場合はそれほど大きな差が出ないこともあります。)

  • 巨大な文字列: 非常に長い文字列に対して置換を行う場合、新しい文字列の生成や正規表現エンジンの処理に時間がかかるのは避けられません。可能であれば、処理対象の文字列サイズを小さくしたり、文字列全体ではなく必要な部分だけを抽出して処理したりすることを検討します。

7.3. split()join() による置換

簡単な複数置換であれば、replace() だけでなく split()join() を組み合わせる方法も有効です。これは、searchValue が文字列の場合に全ての出現箇所を置換したい場合に特にシンプルです。

“`javascript
const text = “apple, banana, apple, cherry”;

// “apple” を “FRUIT” に置換 (split/joinを使用)
const newText = text.split(“apple”).join(“FRUIT”);
console.log(newText); // 出力: “FRUIT, banana, FRUIT, cherry”
“`

この方法は正規表現の複雑なパターンやキャプチャグループには対応できませんが、固定文字列の単純な複数置換には非常に簡潔な記述となります。どちらの方法を選ぶかは、状況や可読性、パフォーマンスの要件によって判断すると良いでしょう。

8. よくある落とし穴と解決策

replace() を使う際によくある疑問や間違い、その解決策をまとめます。

8.1. 「なぜか最初の1つしか置き換わらない」

原因: searchValue に文字列リテラルを指定しているか、または正規表現を指定しているが g (global) フラグを付けていない。

javascript
const text = "one two one three";
text.replace("one", "uno"); // 文字列リテラル -> 最初の1つだけ
text.replace(/one/, "uno"); // 正規表現だが g フラグなし -> 最初の1つだけ

解決策: 全てのマッチを置換したい場合は、searchValue に必ず正規表現を使用し、g フラグを付ける。

javascript
const text = "one two one three";
const newText = text.replace(/one/g, "uno"); // g フラグ付き正規表現 -> 全て置換
console.log(newText); // 出力: "uno two uno three"

8.2. 「大文字小文字が区別されてしまう」

原因: 大文字小文字を区別せずにマッチングを行いたいのに、正規表現に i (ignore case) フラグを付けていない。

javascript
const text = "Apple BANANA apple";
text.replace(/apple/g, "fruit"); // i フラグなし -> "Apple", "BANANA" は置換されない

解決策: 大文字小文字を区別しない置換を行いたい場合は、正規表現に i フラグを付ける。通常、全てのマッチを大文字小文字区別なく置換したいことが多いので、gi フラグを組み合わせることが一般的。

javascript
const text = "Apple BANANA apple";
const newText = text.replace(/apple/gi, "FRUIT"); // gi フラグ付き正規表現
console.log(newText); // 出力: "FRUIT BANANA FRUIT"

8.3. 置換文字列内の特殊文字(特に $)が意図せず解釈される

原因: newValue に含まれる $ が、特殊な置換パターンとして解釈されてしまう。例えば、リテラルの $ を挿入したいのに $$ とエスケープしていない。

javascript
const text = "Price is 10.";
// $10 という文字列を挿入したい
const newText = text.replace("10", "$10"); // $10 がキャプチャグループ1として解釈されようとする (正規表現でないため動作は不安定またはエラーになりうる)

解決策: リテラルの $ を置換文字列に含めたい場合は、$$ と記述してエスケープする。

javascript
const text = "Price is VALUE.";
// "VALUE" を "$10" というリテラル文字列に置換したい
const newText = text.replace("VALUE", "$$10");
console.log(newText); // 出力: "Price is $10."

キャプチャグループを参照したい場合など、意図的に特殊パターンを使っている場合は問題ありませんが、単にリテラルの $ を入れたい場合は注意が必要です。

8.4. 動的に生成した文字列を searchValue の文字列として使いたいが、特殊文字をエスケープする必要がある

原因: ユーザー入力や外部データから取得した文字列をそのまま searchValue (文字列リテラルとして)に使うと、もしその文字列に正規表現の特殊文字が含まれていた場合、意図しないマッチングが発生してしまう。

“`javascript
// 例: ユーザーが検索したい文字列として “a.c” を入力したとする
const userInput = “a.c”; // 正規表現では ‘.’ は任意の一文字を意味する
const text = “abc axc a.c”;

// 文字列リテラルとして検索したいのに…
const result = text.replace(userInput, “match”); // ‘.’ が任意の一文字として解釈されてしまう
console.log(result); // 出力: “abc axc match” (axcもマッチしてしまう)
“`

解決策: 動的な文字列を searchValue に指定する場合、意図せず正規表現として解釈されないように、正規表現の特殊文字をエスケープする必要がある。または、RegExp コンストラクタを使用して正規表現オブジェクトを作成し、その際に文字列をエスケープする。

文字列を正規表現として安全にエスケープする関数は以下のように作成できます。

“`javascript
function escapeRegExp(string) {
// 正規表現の特殊文字をエスケープするためのパターン
return string.replace(/[.*+?^${}()|[]\]/g, ‘\$&’); // $& はマッチした文字全体を指す特殊パターン
}

// ユーザー入力などを安全に正規表現として使う
const userInput = “a.c”;
const text = “abc axc a.c”;

const escapedInput = escapeRegExp(userInput); // “a.c” になる
const regex = new RegExp(escapedInput, ‘g’); // ‘g’フラグを付けて全て置換したい場合

const result = text.replace(regex, “MATCH”);
console.log(result); // 出力: “abc axc MATCH” (a.cだけがマッチ)
“`

ユーザーが入力した文字列などを searchValue に使う場合は、このエスケープ処理を忘れずに行うことが非常に重要です。

9. まとめ

JavaScriptの String.prototype.replace() メソッドは、文字列操作における非常に強力で柔軟なツールです。この記事では、その機能を以下の点から詳細に解説しました。

  • 基本: string.replace(searchValue, newValue) の構文。元の文字列は変更されず、新しい文字列を返す。
  • 文字列検索: searchValue に文字列を指定すると、最初に見つかったマッチのみを大文字小文字を区別して置換する。
  • 正規表現検索: searchValue に正規表現を指定すると、パターンによる柔軟な検索が可能。特に g フラグによる複数置換と i フラグによる大文字小文字を区別しない置換が重要。
  • 置換文字列の特殊パターン: newValue が文字列の場合、$$, $&, $``,$’,$nなどの特殊パターンを使ってマッチ結果を参照し、複雑な置換文字列を生成できる。キャプチャグループ$n` は正規表現と組み合わせて文字列の構造を再配置するのに強力。
  • 置換関数: newValue に関数を指定すると、マッチごとに動的に置換文字列を生成できる。マッチした文字列、キャプチャグループ、位置などの情報を受け取って処理を実行できるため、最も柔軟な置換が可能。
  • 応用例: 複数パターンの置換、サニタイズ、特定のパターンの削除、簡易テンプレートなど、様々な用途に応用できる。
  • 注意点: 元の文字列が不変であること、正規表現のパフォーマンス、文字列検索と split().join() の比較などを理解しておく必要がある。
  • よくある落とし穴: 最初のマッチしか置換されない、大文字小文字の問題、置換文字列の $, 動的な文字列を正規表現として使う場合のエスケープなど、具体的な解決策と共に紹介した。

replace() メソッドは、これらの機能を組み合わせることで、非常に多様な文字列操作に対応できます。まずは基本的な文字列置換から使い始め、必要に応じて正規表現や置換関数といった高度な機能にステップアップしていくと良いでしょう。

特に正規表現は replace() メソッドの能力を最大限に引き出す鍵となります。正規表現自体の学習は別途必要ですが、replace() と共に使いこなすことで、JavaScriptでの文字列処理の効率と表現力が飛躍的に向上します。

この記事が、あなたがJavaScriptの replace() メソッドを理解し、日々のコーディングで自信を持って活用するための一助となれば幸いです。

さあ、今日からあなたのコードで replace() メソッドを存分に活用してください!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール