文字列操作と正規表現 — replace・split・slice・正規表現の実践ガイド
フォームの入力値を整える、URLを組み立てる、APIから来たテキストを加工する——文字列操作は配列と並んで毎日触る処理です。
この章では、文字列の「メソッド」とは何かという前提から始めて、slice・includes・replace/replaceAll・split など実務で頻出するメソッドと、テンプレートリテラル・正規表現の基本を用途別に整理します。
学習者文字列のメソッドって数が多くて、毎回MDNを開いて調べてる…。よく使うものだけ整理してほしい。
この章は、その「よく使うもの」を用途別にまとめた早見表のように使えます。前章の配列メソッド完全ガイドと対になる構成です。
そもそも文字列メソッドとは — String.prototype のメソッド
'hello'.toUpperCase() のように、文字列リテラルに対して直接メソッドを呼べるのはなぜでしょうか。文字列は本来オブジェクトではなくプリミティブ値なのに、です。
const s = 'hello';
console.log(typeof s); // "string"(オブジェクトではない)
s.toUpperCase(); // なのにメソッドが呼べるこれは、メソッドを呼んだ瞬間にJavaScriptが文字列を一時的に String オブジェクトに包む(自動ボックス化) からです。メソッドは String.prototype に定義されており、そこから借りて実行されます。配列が Array.prototype からメソッドを借りるのと同じ仕組みです。
console.log(String.prototype.hasOwnProperty('toUpperCase')); // true
先生だから「文字列メソッド」=「String.prototype のメソッド」。配列のときと同じ考え方だね。

文字列はイミュータブル — すべてのメソッドは「新しい文字列」を返す
最重要の前提です。文字列は一度作ると中身を変更できません(イミュータブル)。 そのため、replace や toUpperCase などのメソッドは元の文字列を変えず、加工した新しい文字列を返します。
const original = 'hello';
const upper = original.toUpperCase();
console.log(upper); // "HELLO"(新しい文字列)
console.log(original); // "hello"(元はそのまま)
学習者text.replace(...) を呼んだのに元の変数が変わってない…!バグかと思った。
バグではありません。戻り値を受け取らないと結果は捨てられます。const result = text.replace(...) のように必ず戻り値を使うのが鉄則です。
テンプレートリテラル — 変数を埋め込む
バッククォート(`)で囲むと、${...} の中に変数や式を埋め込めます。文字列の + 連結より圧倒的に読みやすく、現在の標準です。
const name = '太郎';
const age = 25;
// ❌ + 連結は読みにくい
const a = 'こんにちは、' + name + 'さん(' + age + '歳)';
// ⭕ テンプレートリテラル
const b = `こんにちは、${name}さん(${age}歳)`;
// 改行もそのまま書ける
const html = `
<p>${name}</p>
<p>${age}</p>
`;抽出 — slice / substring / at
文字列の一部を切り出します。実務では slice を覚えておけばほぼ事足ります。
構文: str.slice(start, end?)
| 引数 | 説明 |
|---|---|
start | 切り出しを始める位置(0始まり)。マイナスを指定すると末尾から数える |
end(省略可) | 切り出しを終える位置。その手前までが対象で end 自身は含まない。省略すると末尾まで |
戻り値: 切り出した新しい文字列(元の文字列は変わらない)
at は1文字を取り出すメソッドで、構文は str.at(index)。index にマイナスを指定すると末尾から数えられます(-1 で最後の文字)。
const str = 'JavaScript';
str.slice(0, 4); // "Java"(0番目から4番目の手前まで)
str.slice(4); // "Script"(4番目から最後まで)
str.slice(-6); // "Script"(末尾から6文字。マイナス指定が便利)
str.at(0); // "J"(最初の文字)
str.at(-1); // "t"(最後の文字。str[str.length - 1] より簡潔)検索 — includes / indexOf / startsWith / endsWith
文字列が含まれているか・どこにあるかを調べます。いずれも「探したい文字列」を引数に渡すだけです。
| メソッド | 引数 | 戻り値 |
|---|---|---|
str.includes(searchString) | searchString:探す文字列 | boolean(含まれるか) |
str.indexOf(searchString) | searchString:探す文字列 | 最初に現れた位置(数値)。見つからなければ -1 |
str.startsWith(searchString) | searchString:探す文字列 | boolean(前方一致するか) |
str.endsWith(searchString) | searchString:探す文字列 | boolean(後方一致するか) |
const url = 'https://example.com/users/1';
url.includes('users'); // true(含まれるか → boolean)
url.startsWith('https'); // true(前方一致)
url.endsWith('.com'); // false
url.indexOf('users'); // 20(位置。見つからなければ -1)置換 — replace / replaceAll
文字列を置き換えます。実務で非常によく使う一方、ハマりやすい挙動があるので丁寧に見ます。
構文: str.replace(pattern, replacement)
| 引数 | 渡せるもの | 説明 |
|---|---|---|
pattern(第1引数) | 文字列 / 正規表現 | 何を置き換えるか。文字列を渡すと最初にマッチした1件だけが対象になる(要注意) |
replacement(第2引数) | 文字列 / 関数 | 何に置き換えるか。文字列の中で $1・$2… で正規表現のキャプチャ(後述)を参照でき、関数を渡すとマッチした値から動的に計算できる |
戻り値: 置換後の新しい文字列(元の文字列は変わらない)
replaceAll(pattern, replacement) も引数の意味は同じで、違いはマッチしたすべてを置き換える点だけです。
この2つの引数(pattern と replacement)にそれぞれ何を渡せるかで挙動が変わります。パターン別に見ていきましょう。
基本(文字列で指定すると「最初の1件」だけ)
const text = 'りんご, りんご, みかん';
text.replace('りんご', 'ぶどう');
// "ぶどう, りんご, みかん" ← 最初の1件だけ置換される!
学習者全部置き換えたいのに、1個目しか変わらない…!なんで?
replace に文字列を渡すと、置換されるのは最初にマッチした1件だけです。これは初心者がほぼ必ず引っかかるポイントです。すべて置換するには次の2つの方法があります。
すべて置換する — replaceAll(推奨)
const text = 'りんご, りんご, みかん';
text.replaceAll('りんご', 'ぶどう');
// "ぶどう, ぶどう, みかん" ← 全部置換されるreplaceAll(ES2021)が最もシンプルで読みやすい方法です。古い環境向けには、後述の正規表現に g(グローバル)フラグを付ける方法もあります。
// 正規表現 + g フラグでも全置換できる(replaceAll が使えない環境向け)
text.replace(/りんご/g, 'ぶどう');正規表現で柔軟に置換 + キャプチャ($1)
第1引数に正規表現を渡すと、パターンマッチで置換できます。() でグループ化した部分は、置換文字列の中で $1・$2… として参照できます。
// 2024-01-31 → 2024/01/31 に変換
'2024-01-31'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$1/$2/$3');
// "2024/01/31"コールバックで動的に置換
置換後の値を関数で計算することもできます。マッチした文字列が引数で渡されます。
// 各単語の先頭を大文字に
'hello world'.replace(/\b\w/g, (char) => char.toUpperCase());
// "Hello World"大文字小文字・空白の処理
'Hello'.toUpperCase(); // "HELLO"
'Hello'.toLowerCase(); // "hello"
' hello '.trim(); // "hello"(前後の空白を除去)
' hello '.trimStart(); // "hello "
'5'.padStart(3, '0'); // "005"(左を0埋め。ゼロパディングの定番)
'5'.padEnd(3, '*'); // "5**"padStart / padEnd の構文は str.padStart(targetLength, padString?) です。
| 引数 | 説明 |
|---|---|
targetLength | 最終的に揃えたい文字数。元の文字列がすでにこの長さ以上なら何も埋めない |
padString(省略可) | 埋めるのに使う文字。デフォルトは半角スペース |
分割と結合 — split / join
文字列を配列に分割(split)し、配列を文字列に結合(join)します。配列処理との橋渡しになる重要メソッドです。
| メソッド | 引数 | 戻り値 |
|---|---|---|
str.split(separator, limit?) | separator:区切り文字('' を渡すと1文字ずつ) / limit(省略可):取り出す最大要素数 | 配列 |
arr.join(separator?) | separator(省略可):要素をつなぐ文字(省略すると ,) | 文字列 |
// 分割:文字列 → 配列
'a,b,c'.split(','); // ["a", "b", "c"]
'hello'.split(''); // ["h", "e", "l", "l", "o"](1文字ずつ)
// 結合:配列 → 文字列
['a', 'b', 'c'].join('-'); // "a-b-c"
// 組み合わせ:CSVの各値をトリムする
' a , b , c '
.split(',')
.map((s) => s.trim())
.join(','); // "a,b,c"正規表現の基本
正規表現(RegExp)は「文字列のパターン」を表す仕組みです。replace や検索と組み合わせて使います。組み合わせるメソッドの引数は次のとおりです。
| メソッド | 引数 | 戻り値 |
|---|---|---|
regex.test(str) | str:調べる文字列 | boolean(マッチするか) |
str.match(regex) | regex:正規表現 | マッチ情報。g なしなら最初のマッチ+キャプチャ、なければ null |
str.matchAll(regex) | regex:正規表現(g フラグ必須) | すべてのマッチのイテレータ |
const re = /\d{3}-\d{4}/; // 「数字3桁-数字4桁」のパターン(郵便番号など)
re.test('123-4567'); // true(マッチするか → boolean)
'郵便番号は123-4567です'.match(re); // マッチ部分の情報を返す
// すべてのマッチを取り出す(matchAll + g フラグ)
const text = 'tel: 03-1234, 06-5678';
[...text.matchAll(/\d{2}-\d{4}/g)].map((m) => m[0]);
// ["03-1234", "06-5678"]よく使うパターン部品:
| 記法 | 意味 |
|---|---|
\d | 数字1文字(\D は数字以外) |
\w | 英数字・アンダースコア |
\s | 空白文字 |
. | 任意の1文字 |
+ | 直前を1回以上 |
* | 直前を0回以上 |
? | 直前を0回または1回 |
{n} | 直前をn回 |
^ / $ | 行頭 / 行末 |
[abc] | a,b,cのいずれか |
| ` | ` |
主なフラグ:g(全マッチ)、i(大文字小文字を無視)、m(複数行)。
メソッド早見表
| やりたいこと | メソッド | 戻り値 |
|---|---|---|
| 一部を切り出す | slice | 新しい文字列 |
| 含まれるか調べる | includes | boolean |
| 前方/後方一致 | startsWith / endsWith | boolean |
| 最初の1件を置換 | replace | 新しい文字列 |
| すべて置換 | replaceAll | 新しい文字列 |
| 大文字/小文字化 | toUpperCase / toLowerCase | 新しい文字列 |
| 前後の空白除去 | trim | 新しい文字列 |
| 区切って配列に | split | 配列 |
| パターンに一致するか | 正規表現 + test | boolean |
よくあるハマりどころ

1. replace が最初の1件しか置換しない
前述のとおり、文字列指定の replace は最初の1件だけ。全置換は replaceAll(または /.../g)を使います。
2. 戻り値を使い忘れる
文字列はイミュータブル。text.trim() だけでは何も起きません。text = text.trim() のように戻り値を代入します。
3. length と「見た目の文字数」がずれる
絵文字や一部の文字は、length 上は2以上としてカウントされます。
'👨👩👧'.length; // 8(見た目は1文字なのに)正確な「文字単位」で数えたい場合は [...str].length(スプレッドで分割)や Intl.Segmenter を使います。
ちゃんと使うためのポイント
- 文字列メソッドは
String.prototypeのメソッド。文字列はイミュータブルで、メソッドは新しい文字列を返す(戻り値を必ず使う) - 連結はテンプレートリテラル(
`${x}`)、抽出はslice、含有判定はincludesが基本 - 全置換は
replaceAll。replaceに文字列を渡すと最初の1件だけなので注意 - 入力値は
trim()で前後空白を落としてから扱う - 複雑なパターンの検索・置換は正規表現と組み合わせる
次の章では、分割代入とスプレッド構文 を扱います。split で作った配列の受け取りなど、ここで学んだ文字列操作とも自然につながります。