【JavaScript】replace()メソッドの使い方と応用【文字列を置換する】

[**こちらのアプリ**](https://readability-checker.khufrudamonotes.com/free-conversion)↓を作るときに**JavaScript**の**replace()メソッド**周りで詰まったので、忘備録を書きます。

https://readability-checker.khufrudamonotes.com/free-conversion

# replace()メソッドの基本的な使い方

まず、JavaScriptの**replace()メソッド**は、置換したいテキストに対して

```js
replace('置換したい文字列','置換後の文字列')
```

みたいに使います。

## 記述例

たとえばこんな感じ。

```js
//元のテキスト
let text = 'おはようございます。今日はいい天気ですね。';

//replace()を使って「おはようございます」を「こんにちは」に置換する。
text = text.replace('おはようございます', 'こんにちは');

//console.logで出力を確認する。
console.log(text);
//結果：こんにちは。今日はいい天気ですね。
```

「**おはようございます**」が「**こんにちは**」置換されるはずです。

## しかし、一か所しか置換されない！

しかし、上記の書き方だと少し問題があります。

一か所しか置換されないのです。

たとえば、

```js
//元のテキスト
let text = 'しんぶんし。下から読んでも、しんぶんし。';
```

元のテキストに置換したい文字列が**2か所以上ある**場合…

```js
let text = 'しんぶんし。下から読んでも、しんぶんし。';

text = text.replace('しんぶんし', 'イカとカイ');

console.log(text);
//結果：イカとカイ。下から読んでも、しんぶんし。
```

先ほどの書き方だと、最初の「しんぶんし」しか置換されません。

どちらの「**しんぶんし**」も「**イカとカイ**」に置換したい！

## //gで囲む。

結論から言えば、「正規表現のgオプション」を使うと解決します。

やり方は、置換したい文字列を「**'　'**」の代わりに「**/　/g**」で囲むだけ。

```js
let text = 'しんぶんし。下から読んでも、しんぶんし。';

text = text.replace(/しんぶんし/g, 'イカとカイ');

console.log(text);
//結果：イカとカイ。下から読んでも、イカとカイ。
```

これで全ての「**しんぶんし**」が「**イカとカイ**」に置換できました！

# 変数で置換したい文字列を指定したい。

さて。

ここまでは、「**置換したい文字列**」も「**置換後の文字列**」も直接replace()メソッドの中に文字列を書き込んでいました。

```js
replace(/置換したい文字列/g, '置換後の文字列')
```

これを**変数**で指定したくなってきます。

しかし…

```js
let text = 'しんぶんし。下から読んでも、しんぶんし。';
let before_word = 'しんぶんし'
let after_word = 'イカとカイ'

text = text.replace(/before_word/g, after_word);
```

こんな感じ↑に馬鹿正直に変数に置き換えて書いてもダメです。

置換したい文字列が"**before_wordそのもの**"になるので、上手くいきません。

では、どうするか。

## RegExp オブジェクトのコンストラクター関数を呼び出す。

**コンストラクター関数**というやつを使います。

つまり、こうやって書けばOK！

```js
text = text.replace(new RegExp(before_word, "g"), after_word);
```

これで

```js
//元のテキスト
let text = 'しんぶんし。下から読んでも、しんぶんし。';
let before_word = 'しんぶんし'
let after_word = 'イカとカイ'

text = text.replace(new RegExp(before_word, "g"), after_word);

console.log(text);
//結果：イカとカイ。下から読んでも、イカとカイ。
```

変数を使って**replace()メソッド**を使えました！

# しかし…

これで大体上手くいくはずです。

しかし、**メタ文字**（正規表現で使用する特殊文字）が誤った形で「**置換したい文字列**」に含まれていた場合にエラーになります。

たとえば、半角の括弧 → (だけとかです。

https://qiita.com/katsukii/items/1c1550f064b4686c04d4

### 対策1：先に正規表現でエスケープが必要な文字を置換する。

対策の一つ目は**最初から正規表現を封じる**方法。

やり方は、**replace()メソッド**を**もう一つ使います。**

```js
before_word = before_word.replace(/&#91;.**+?^${}()|&#91;\]\\]/g, '\\$&amp;')
```

前もって**正規表現をエスケープさせて**エラーになるのを防ぐわけです。

```js
let text = 'しんぶんし。下から読んでも、しんぶんし。';
let before_word = '(しんぶんし'
let after_word = 'イカとカイ'

before_word = before_word.replace(/&#91;.**+?^${}()|&#91;\]\\]/g, '\\$&amp;')
text = text.replace(new RegExp(before_word, "g"), after_word);

console.log(text);
//結果：しんぶんし。下から読んでも、しんぶんし。
```

つまりこうです。

これで「**(しんぶんし**」の「**(**」でエラーにならずに済みます。

## 対策2:エラーになったときだけ対応する。

でも、できれば正規表現も使いたいよ…。

そんな時は、**try-catch文**を使ってみます。

```js
try {
    text = text.replace(new RegExp(before_word, "g"), after_word);
} catch (e) {
    before_word = before_word.replace(/&#91;.**+?^${}()|&#91;\]\\]/g, '\\$&amp;')
    text = text.replace(new RegExp(before_word, "g"), after_word);
};
```

try{　}の中身が通常の処理、catch(e){　}の中身がエラー時の処理です。

エラー時だけ**正規表現をエスケープ**させるわけです。

```js
let text = 'しんぶんし。下から読んでも、しんぶんし。';
let before_word = '(しん.ん.)'
let after_word = '!!$1!!'

try {
    text = text.replace(new RegExp(before_word, "g"), after_word);
} catch (e) {
    before_word = before_word
        .replace(/&#91;.**+?^${}()|&#91;\]\\]/g, '\\$&amp;')
    text = text.replace(new RegExp(before_word, "g"), after_word);
};

console.log(text);
//結果：!!しんぶんし!!。下から読んでも、!!しんぶんし!!。
```

これで**正規表現を使った置換**もエラーを心配せずに実行できます。（たぶん）

### ちなみに…

上記のコードの「\$1」は、置換前の(　)の中身がそのまま置換後へ反映される書き方です。

また、もし置換前に(　)で囲まれた文字列が2つ以上あれば、以下のように書けばそれぞれ反映されます。
```js
replace('(しん)ぶん(し)', '!!$1!!$2')
//結果：!!しん!!し
```

## 配列を使って一気に置換したいなぁ…

最後に**配列**を作って、一気に置換をする方法を紹介します。

まず、「**置換したい文字列**」と「**置換後の文字列**」を入れる**2次元配列**を作ります。

```js
//置換したいテキストの2次元配列
let replacement_words = [
    ['(しん.ん.)', '!!$1!!'],
    ['しんぶんし', 'イカとカイ'],
    ['下から読んでも', 'とてもおいしい'],
];
```

あとは、配列の長さを「.length」で取得して**for文**を回せばOKです。

```js
for (let i = 0; i < replacement_words.length; i++) {
    text = text
        .replace(new RegExp(replacement_words[i][0], "g"), replacement_words[i][1]);
};
```

これで、配列の長さが変わっても（文字列を追加や削除をしても）大丈夫です。

# まとめ

ここまでの内容をまとめたコードです。

```js
//置換したいテキストの2次元配列
let replacement_words = [
    ['(しん.ん.)', '!!$1!!'],
    ['しんぶんし', 'イカとカイ'],
    ['下から読んでも', 'とてもおいしい'],
];

try {
    for (let i = 0; i < replacement_words.length; i++) {
        text = text
            .replace(new RegExp(replacement_words[i][0], "g"), replacement_words[i][1]);
    };
} catch (e) {
    //エラー時の処理
    for (let i = 0; i < replacement_words.length; i++) {
        replacement_words[i][0] = replacement_words[i][0]
            .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
        text = text
            .replace(new RegExp(replacement_words[i][0], "g"), replacement_words[i][1]);
    };
};

console.log(text);
//結果：!!イカとカイ!!。とてもおいしい、!!イカとカイ!!。
```

# さいごに

というわけで、**JavaScript**のreplace()メソッドの使い方と応用の忘備録でした。

色々調べながら辿り着いた方法なので、もし間違いがあれば教えていただけると幸いです。

では！(๑˃̵ᴗ˂̵)و

## 参考サイト・URL

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions

https://qiita.com/ue5963/items/bd8e32ac9e6b12aa7fab