JavaScript Tips | 文字列操作:文字列内の指定文字をすべて置換する

JavaScript JavaScript
スポンサーリンク
スポンサーリンク
  1. JavaScriptで文字列内の指定文字をすべて置換する方法
    1. 最初に覚える基本形
  2. ステップ1:置換に必要な3つの情報を整理する
  3. ステップ2:置換結果を必ず受け取る
    1. 元の文字列は変更されない
    2. 同じ変数に結果を入れ直す場合
  4. ステップ3:実務でよく使う置換を試す
    1. 例題1:文章内の表記を統一する
    2. 例題2:不要な文字をすべて削除する
    3. 例題3:全角スペースを半角スペースに統一する
  5. ステップ4:replace()との違いを理解する
  6. ステップ5:実務向けのユーティリティ関数にまとめる
    1. 入力を検証し、文字をそのまま置換するテンプレート
    2. なぜ置換内容を関数で返しているのか
  7. ステップ6:正規表現を使う場面を見分ける
    1. 複数種類の改行を統一する
    2. replaceAll()に正規表現を渡す場合もgが必要
    3. 検索文字列を安易に正規表現へ変換しない
  8. ステップ7:境界条件を確認する
    1. 検索対象がない場合は内容が変わらない
    2. 空の検索文字列には独特の動作がある
    3. 重なった一致や、置換後の文字を繰り返し置換する処理ではない
  9. ステップ8:テストで期待する動作を固定する
  10. 実務で特に注意したい安全性と運用
    1. 文字列置換とセキュリティ対策を混同しない
    2. 複数の置換をつなげると順番が結果に影響する
    3. 実行環境とデータ量を確認する
  11. まとめ:文字列の全置換は、基本形と入力の扱いを押さえる

JavaScriptで文字列内の指定文字をすべて置換する方法

JavaScriptで文字列内の指定文字をすべて置き換えるには、基本的にreplaceAll()を使います。例えば、商品コードの区切り文字を変更する、文章内の表記を統一する、入力データから不要な文字を削除するといった処理に利用できます。指定できるのは1文字だけではなく、「株式会社」のような複数文字のまとまりも対象です。

この記事では、最も簡単な書き方から始めて、処理の仕組み、実務向けのユーティリティ関数、間違えやすい注意点の順に説明します。特に重要なのは、置換結果を受け取ること、検索文字列と正規表現を区別すること、外部から受け取った置換文字列を意図どおりに扱うことです。

最初に覚える基本形

// 商品コードに含まれるハイフンを、すべてアンダースコアに置き換えます。
const productCode = "ITEM-2026-001";
const result = productCode.replaceAll("-", "_");

// 置換後の文字列を表示します。
console.log(result); // "ITEM_2026_001"
JavaScript

replaceAll("-", "_")は、「ハイフンを探して、見つかった箇所をすべてアンダースコアに置き換える」という意味です。ここでのハイフンは通常の文字列として指定しているため、特別な記号として解釈されません。

ステップ1:置換に必要な3つの情報を整理する

文字列の置換を考えるときは、まず「元の文字列」「探す文字列」「置き換え後の文字列」を分けて整理します。この3つを明確にすると、処理をコードに落とし込みやすくなります。

  • 元の文字列:処理対象のデータです。例として"東京-大阪-福岡"を使います。
  • 探す文字列:変更したい部分です。今回は"-"です。
  • 置き換え後の文字列:変更後に使う文字列です。今回は"/"です。
// 置換に必要な情報を、それぞれ変数に分けます。
const text = "東京-大阪-福岡";
const search = "-";
const replacement = "/";

// text内のsearchを、すべてreplacementに置き換えます。
const result = text.replaceAll(search, replacement);

// 結果を確認します。
console.log(result); // "東京/大阪/福岡"
JavaScript

textの後ろにあるドットは、その文字列が持つ機能を呼び出すための記号です。replaceAll()のように、値に対して呼び出す機能を「メソッド」といいます。丸括弧の中に渡す値は「引数」と呼び、最初の引数が検索対象、2番目の引数が置換内容です。

ステップ2:置換結果を必ず受け取る

元の文字列は変更されない

replaceAll()は元の文字列を書き換えるのではなく、置換後の新しい文字列を返します。そのため、呼び出すだけでは元の変数の内容は変わりません。これはreplace()でも同じです。

// 元の文字列を用意します。
const original = "A-A-A";

// 置換を実行しても、結果を保存していません。
original.replaceAll("A", "B");

// original自体は変わりません。
console.log(original); // "A-A-A"

// 戻り値を変数に保存すると、置換結果を利用できます。
const converted = original.replaceAll("A", "B");
console.log(converted); // "B-B-B"
JavaScript

元の値と変換後の値を両方残しておくと、変換前後の比較や不具合の調査がしやすくなります。業務データの加工では、元データを保持する設計が役立つ場面も多くあります。

同じ変数に結果を入れ直す場合

変換後の値だけを使う場合は、置換結果を同じ変数に代入できます。ただし、再代入する変数はconstではなくletで宣言します。

// 後で再代入するため、letを使います。
let code = "A-B-C";

// 置換結果をcodeに入れ直します。
code = code.replaceAll("-", "_");

// 変数には変換後の文字列が入っています。
console.log(code); // "A_B_C"
JavaScript

ここで行っているのは、文字列そのものの書き換えではなく、変数に新しい文字列を入れ直す処理です。この違いを理解すると、「置換したはずなのに変わらない」という初歩的なトラブルを避けやすくなります。

ステップ3:実務でよく使う置換を試す

例題1:文章内の表記を統一する

社内文書や商品説明では、同じ表記をまとめて変更したいことがあります。検索対象には複数文字も指定できるため、名称や用語の統一にも使えます。

// 文中に同じ表記が複数登場します。
const text = "申込はこちら。申込後に確認メールを送信します。";

// 「申込」を、すべて「お申し込み」に置き換えます。
const result = text.replaceAll("申込", "お申し込み");

console.log(result);
// "お申し込みはこちら。お申し込み後に確認メールを送信します。"
JavaScript

ただし、文字列の置換は文章の意味を理解しません。例えば「申込」を置換すると、「申込書」の一部にも一致します。人名、会社名、契約文言などを扱う場合は、部分一致によって変更してはいけない箇所まで変わらないかを確認する必要があります。

例題2:不要な文字をすべて削除する

文字の削除は、置換後の文字列に""を指定して実現できます。""は、文字が1つも入っていない「空文字列」です。

// 半角スペースが含まれる管理コードです。
const code = " AB 12 34 ";

// 半角スペースを空文字列に置き換えて削除します。
const result = code.replaceAll(" ", "");

console.log(result); // "AB1234"
JavaScript

この処理で消えるのは、指定した半角スペースだけです。全角スペースやタブ、改行まで自動的に削除されるわけではありません。また、氏名や住所の空白には意味がある場合もあります。データを整えるつもりで必要な区切りまで失わないように、削除する文字の範囲を先に決めることが重要です。

例題3:全角スペースを半角スペースに統一する

// 名前の間に全角スペースが入っています。
const name = "山田 太郎";

// 全角スペースを半角スペースに置き換えます。
const result = name.replaceAll(" ", " ");

console.log(result); // "山田 太郎"
JavaScript

全角スペースと半角スペースは見た目が似ていますが、異なる文字です。入力データの検証や検索条件の整備では、このような見えにくい違いが結果に影響します。置換しても変化がない場合は、「同じように見えるだけで別の文字ではないか」という観点でも確認するとよいでしょう。

ステップ4:replace()との違いを理解する

replace()とreplaceAll()は名前が似ていますが、検索対象に通常の文字列を渡した場合の動作が異なります。replace()は最初の一致だけを置換し、replaceAll()は一致した箇所をすべて置換します。

// 同じ文字が3回登場する文字列です。
const text = "A-A-A";

// 通常の文字列を指定したreplace()では、最初の一致だけが変わります。
console.log(text.replace("A", "B")); // "B-A-A"

// replaceAll()では、一致した箇所がすべて変わります。
console.log(text.replaceAll("A", "B")); // "B-B-B"

// replace()でも、gフラグ付きの正規表現なら全置換できます。
console.log(text.replace(/A/g, "B")); // "B-B-B"
JavaScript

初心者のうちは、決まった文字をそのまますべて置換したいなら、文字列を指定したreplaceAll()を選ぶと意図が伝わりやすくなります。一方、「数字をすべて探す」「複数種類の空白をまとめて探す」といった条件付きの検索には、正規表現が役立ちます。

ステップ5:実務向けのユーティリティ関数にまとめる

入力を検証し、文字をそのまま置換するテンプレート

複数の画面や処理で同じ置換を使う場合は、共通関数にまとめると便利です。次の関数では、3つの引数を文字列に限定し、空の検索文字列を禁止します。また、置換後の文字列に特殊な記号が含まれていても、その内容をそのまま挿入するようにしています。

/**
 * 指定文字列を、文字どおりにすべて置換します。
 * 検索文字列には、空ではない文字列を指定してください。
 *
 * @param {string} text - 元の文字列
 * @param {string} search - 探す文字列
 * @param {string} replacement - 置き換え後の文字列
 * @returns {string} 置換後の文字列
 */
function replaceAllLiteral(text, search, replacement) {
  // 想定外の型を、処理の入口で検出します。
  if (
    typeof text !== "string" ||
    typeof search !== "string" ||
    typeof replacement !== "string"
  ) {
    throw new TypeError("すべての引数に文字列を指定してください。");
  }

  // 意図しない文字の挿入を防ぐため、空の検索文字列を禁止します。
  if (search === "") {
    throw new RangeError("検索文字列を空にすることはできません。");
  }

  // 関数で返した文字列は、置換用の特殊記号として解釈されません。
  return text.replaceAll(search, () => replacement);
}

// 商品コードの区切りを統一します。
console.log(replaceAllLiteral("AB-12-CD", "-", "_"));
// "AB_12_CD"

// 不要な文字を削除します。
console.log(replaceAllLiteral("A B C", " ", ""));
// "ABC"
JavaScript

throwは、問題がある入力を受け取ったことをエラーとして知らせるための構文です。型の間違いにはTypeError、この関数で許可していない値にはRangeErrorを使っています。入力の不備を早めに検出することで、後続処理に不正なデータが流れ込むのを防ぎやすくなります。

また、この関数は正規表現を受け付けません。「指定された文字列を、そのまま探して、そのまま置き換える」という役割に限定しています。万能な関数を目指すより、何を保証する関数なのかを明確にするほうが、業務コードでは保守しやすくなります。

なぜ置換内容を関数で返しているのか

() => replacementは、replacementの値を返す小さな関数です。置換処理では、一致した箇所ごとにこの関数が呼び出され、その戻り値が挿入されます。この書き方には、置換文字列に含まれる$の特殊な解釈を避ける目的があります。

replaceAll()の第2引数に文字列を直接渡す場合、$&は「一致した文字列を挿入する」、$$は「ドル記号を1つ挿入する」という意味を持ちます。一方、関数が返した文字列には、これらの置換パターンは適用されません。

// 置換したい文字列と、挿入したい内容を用意します。
const text = "商品: TOKEN";
const replacement = "$&";

// 文字列を直接渡すと、$&は一致した「TOKEN」を意味します。
console.log(text.replaceAll("TOKEN", replacement));
// "商品: TOKEN"

// 関数で返すと、$&という文字がそのまま入ります。
console.log(text.replaceAll("TOKEN", () => replacement));
// "商品: $&"
JavaScript

固定の文章に置き換えるだけなら、この違いが問題にならないこともあります。しかし、利用者が入力した文章や外部サービスから受け取った値を挿入する場合は、内容に$&などが含まれる可能性があります。置換内容を文字どおりに扱いたい実務用途では、関数を渡す方法が適しています。

ステップ6:正規表現を使う場面を見分ける

複数種類の改行を統一する

正規表現は、文字そのものではなく「文字の並び方のルール」を指定する仕組みです。例えば、外部から取り込んだテキストでは、改行が\r\n、\r、\nのいずれかになっていることがあります。これらをまとめて\nへ統一する処理は、正規表現が得意な用途です。

// 異なる改行コードが混在した文字列です。
const text = "1行目\r\n2行目\r3行目\n4行目";

// CRLF、CR、LFを、すべてLFに統一します。
// CRLFを先に書き、2文字をまとめて一致させます。
const normalized = text.replace(/\r\n|\r|\n/g, "\n");

// 改行コードを見やすくするため、JSON形式で表示します。
console.log(JSON.stringify(normalized));
// "1行目\n2行目\n3行目\n4行目"
JavaScript

正規表現内の|は「または」を表します。末尾のgは、最初の一致だけで終わらず、文字列全体を検索するための指定です。なお、この例は代表的な3種類の改行コードを対象にしており、あらゆるUnicodeの改行文字を扱う汎用処理ではありません。

replaceAll()に正規表現を渡す場合もgが必要

replaceAll()に正規表現を渡す場合は、その正規表現にgフラグが必要です。付いていない場合は、最初だけ置換されるのではなく、TypeErrorになります。

// 正規表現を使った全置換では、gを指定します。
const result = "A-A-A".replaceAll(/A/g, "B");

console.log(result); // "B-B-B"

// 次の呼び出しはgがないため、実行するとTypeErrorになります。
// "A-A-A".replaceAll(/A/, "B");
JavaScript

検索文字列を安易に正規表現へ変換しない

正規表現では、ピリオドや括弧などが特別な意味を持ちます。通常の文字を探したいだけなのに正規表現へ変換すると、意図しない範囲に一致する可能性があります。

// ピリオドを含むファイル名です。
const fileName = "report.v1.txt";

// 文字列で指定すると、ピリオドだけを置換します。
console.log(fileName.replaceAll(".", "_"));
// "report_v1_txt"

// 正規表現のドットは、この例では各文字に一致します。
// ピリオドだけでなく、英数字まで置き換わってしまいます。
console.log("a.b".replace(/./g, "_"));
// "___"
JavaScript

検索内容を利用者が入力できる場合は、特に注意が必要です。通常の文字列を探す目的なら、new RegExp()で変換せず、replaceAll(search, ...)のように文字列として渡すことを基本にします。正規表現を受け付ける機能は、通常の文字列検索とは別の機能として設計すると、誤動作や複雑なパターンによる負荷を管理しやすくなります。

ステップ7:境界条件を確認する

検索対象がない場合は内容が変わらない

// ハイフンが含まれていない場合、内容はそのままです。
console.log("ABC".replaceAll("-", "_"));
// "ABC"

// 元の文字列が空でも、検索文字列が空でなければ結果は空文字列です。
console.log("".replaceAll("A", "B"));
// ""
JavaScript

一致する箇所がなくても、それだけでエラーにはなりません。ただし、「必ず管理番号の区切りが含まれるはず」といった業務上の前提がある場合は、置換処理とは別に入力内容を検証する必要があります。

空の検索文字列には独特の動作がある

// 検索文字列が空だと、文字列の両端と間に挿入されます。
console.log("ABC".replaceAll("", "-"));
// "-A-B-C-"
JavaScript

空の検索文字列は、標準のreplaceAll()ではエラーになりません。しかし、一般的な文字置換でこの動作を期待することは多くありません。そのため、先ほどのユーティリティ関数では、検索文字列が空の場合に明示的なエラーを返す設計にしています。

重なった一致や、置換後の文字を繰り返し置換する処理ではない

// 重なり合わない一致を置換します。
console.log("aaa".replaceAll("aa", "X"));
// "Xa"

// 挿入した文字列を、同じ呼び出しで再び置換することはありません。
console.log("a".replaceAll("a", "aa"));
// "aa"
JavaScript

"aaa"では、先頭の"aa"を置換した後に、最後の"a"が残ります。また、置換によって新しく挿入した文字列を、同じ呼び出しの中で繰り返し検索するわけではありません。「変化がなくなるまで置換し続ける処理」とは異なるため、両者を混同しないことが重要です。

ステップ8:テストで期待する動作を固定する

ユーティリティ関数は、複数の処理から利用されます。正常な例だけでなく、空文字列、検索対象がない場合、特殊記号を含む場合も確認しておくと、変更による不具合を発見しやすくなります。次のコードは、先ほどのreplaceAllLiteral()を定義した後に実行します。

// 通常の全置換を確認します。
console.assert(
  replaceAllLiteral("A-A-A", "-", "_") === "A_A_A",
  "区切り文字をすべて置換できること"
);

// 空文字列への置換、つまり削除を確認します。
console.assert(
  replaceAllLiteral("A B C", " ", "") === "ABC",
  "指定文字を削除できること"
);

// 一致しない場合を確認します。
console.assert(
  replaceAllLiteral("ABC", "-", "_") === "ABC",
  "一致しなければ内容が変わらないこと"
);

// 元の文字列が空の場合を確認します。
console.assert(
  replaceAllLiteral("", "-", "_") === "",
  "空の入力を扱えること"
);

// 置換内容の特殊記号を、そのまま挿入できることを確認します。
console.assert(
  replaceAllLiteral("TOKEN", "TOKEN", "$&") === "$&",
  "置換文字列を文字どおりに扱えること"
);

// 空の検索文字列を拒否することを確認します。
let rejectedEmptySearch = false;

try {
  replaceAllLiteral("ABC", "", "_");
} catch (error) {
  // 期待した種類のエラーかを確認します。
  rejectedEmptySearch = error instanceof RangeError;
}

console.assert(
  rejectedEmptySearch,
  "空の検索文字列を拒否できること"
);
JavaScript

console.assert()は、渡した条件が偽だった場合にコンソールへメッセージを出す簡易的な確認手段です。すべて成功しても、成功メッセージが並ぶわけではありません。業務システムで継続的に検証する場合は、プロジェクトで採用しているテストの仕組みに同じケースを組み込むとよいでしょう。

実務で特に注意したい安全性と運用

文字列置換とセキュリティ対策を混同しない

指定文字を置き換えただけで、HTMLやSQLなどに安全に埋め込めるデータになるわけではありません。先ほどの関数名にある「Literal」は、検索内容と置換内容を文字どおりに扱うという意味であり、あらゆる出力先に対する安全性を保証するものではありません。

例えば、利用者が入力した文章をブラウザ上に表示するだけなら、HTMLとして解釈するinnerHTMLではなく、通常のテキストとして扱うtextContentを使う方法が基本になります。

// この例では、入力欄と表示先の要素がページ上に存在することを前提にします。
const input = document.querySelector("#message");
const output = document.querySelector("#preview");

// 要素が見つかった場合だけ処理します。
if (input && output) {
  // 改行を半角スペースへ置換します。
  const converted = input.value.replace(/\r\n|\r|\n/g, " ");

  // 置換結果をHTMLではなく、テキストとして表示します。
  output.textContent = converted;
}
JavaScript

HTMLとしての表示、URLへの組み込み、データベースへの保存では、必要な対策が異なります。文字列置換はデータ加工の道具として使い、出力先に応じた安全なAPIや仕組みを別途選ぶことが重要です。

複数の置換をつなげると順番が結果に影響する

// 最初の置換結果が、次の置換対象になります。
const result = "A"
  .replaceAll("A", "B")
  .replaceAll("B", "C");

// AからBへ、さらにBからCへ変換されます。
console.log(result); // "C"
JavaScript

この動作が目的に合っていれば問題ありません。しかし、「元のAだけをBにし、元のBだけをCにしたい」という要件とは異なります。複数の変換ルールがある場合は、前の変換結果を次のルールで処理してよいのかを確認してください。

実行環境とデータ量を確認する

業務システムでは、利用者のブラウザやサーバーのJavaScript実行環境が統一されているとは限りません。配布前に、対象環境でreplaceAll()を利用できるか確認します。未対応環境が必要な場合は、ポリフィルや代替実装をプロジェクトの方針に合わせて選びます。

また、文字列の置換では新しい文字列が作られます。巨大なファイルに何度も置換を繰り返すと、処理時間やメモリ使用量が問題になる場合があります。数行の入力を整形する処理と、大量の業務データを変換する処理は分けて考え、実際のデータ量で確認することが大切です。

まとめ:文字列の全置換は、基本形と入力の扱いを押さえる

JavaScriptで指定文字をすべて置換する基本は、text.replaceAll(search, replacement)です。まずは元の文字列、検索文字列、置換文字列を分けて考え、返された結果を変数に保存するところから始めます。

  • 固定の文字列を全置換する場合:replaceAll()に文字列を渡します。
  • 指定文字を削除する場合:置換後の文字列に""を指定します。
  • 外部から受け取った置換内容をそのまま使う場合:text.replaceAll(search, () => replacement)を使います。
  • 複数種類の文字や並び方を条件にする場合:正規表現を検討し、全置換ではgフラグを確認します。
  • 共通関数として使う場合:入力の型、空の検索文字列、特殊記号、置換対象がない場合をテストします。
  • 表示や保存につなげる場合:文字列置換とは別に、出力先に応じた安全な方法を選びます。

短いコードでも、検索対象と置換内容の解釈を明確にすれば、実務で安心して使い回せる部品になります。まず基本形で動作を理解し、そのうえで必要な入力検証やテストを追加していくことが、保守しやすいユーティリティを作る近道です。

タイトルとURLをコピーしました