B バイナンス · 世界最大級の暗号資産取引所バイナンス 登録 → AD OKX OKX · 世界の大手暗号資産取引所OKX 登録 → AD
💻 コーディングの基礎 · 第7講 / 10

文字列とテキスト処理 — 文字を切り出し、探し、置き換える

文字列は文字が順番に並んだ値で、JavaScriptには切り出す・探す・分ける・置き換えるメソッドが最初からそろっています。メソッドは元の文字列を変えずに新しい文字列を返すこと、そして`length`が「目に見える文字数」といつも同じとは限らないことが要点です。

⏱ 約20分 ✍️ 確認問題 5問 🧪 コード実習 4題 更新 2026-10-09
🎯 この講義の学習目標
  • length・インデックス・sliceで文字列の一部を取り出せる
  • indexOf・includes・startsWithで、ある文字があるかどうか、どこにあるかを調べられる
  • split・join・trim・replaceAllでテキストを分け、整え、置き換えられる
  • 絵文字や濁点付きの文字でlengthが見た目の文字数と違うことがある理由を説明でき、簡単な正規表現を読める

1.文字列は文字の並びです

第2講で見たように、文字列(string)は引用符で囲んだ値です。ダブルクォートの"りんご"もシングルクォートの'りんご'も同じ文字列で、バッククォートで囲むテンプレート文字列(テンプレートリテラル)は文字列の中に変数の値を埋め込むときに使います。テンプレート文字列の形は下のコードで直接確かめてください。どの引用符を使うにしても、一つのプログラムの中ではそろえたほうが読みやすくなります。

文字列は文字が順番に並んだものなので、第6講の配列と同じように番号(インデックス)で一文字ずつ取り出せます。最初の文字が0番で、s.lengthは文字単位の長さです。そのため最後の文字はs[s.length - 1]で、最近はs.at(-1)のように後ろから数える書き方も使います。存在しない番号を取り出すとエラーではなくundefinedになる点も配列と同じです。

配列と大きく違う点が一つあります。文字列は一度作ると中の文字を変えられません。s[0] = "z"のように一文字を書き換えようとすると、この練習場のように厳格モード(strict mode)で動くコードではTypeErrorになります。そのため、この講義のメソッドはどれも元の文字列を書き換えず、「新しい文字列」を返します。結果を使うには、必ず変数で受け取っておく必要があります。

インデックスは0から、存在しない番号はundefined
const name = "健太";
const fruit = 'りんご';
const msg = `${name}さん、${fruit}3個`;
console.log(msg);
console.log(msg.length);
console.log(msg[0], msg.at(-1), msg[99]);
出力
健太さん、りんご3個
10
健 個 undefined
メソッドは新しい文字列を返す — 受け取らなければ元のまま
let s = "  hello  ";
s.trim();
console.log("[" + s + "]");
s = s.trim();
console.log("[" + s + "]");
出力
[  hello  ]
[hello]
メソッドを呼んだのに結果が変わらないときは、まず返ってきた値を変数に入れたかどうかを確かめてください。初心者がいちばんよくするミスです。

2.文字数の落とし穴 — 絵文字と濁点

lengthは「目に見える文字数」ではなく、JavaScriptが文字列を保存する単位(UTF-16のコード単位)の個数です。ふだん使うひらがな・カタカナ・漢字はほとんどが1文字1単位なので、"日本".lengthは2、"こんにちは".lengthは5と、見たとおりになります。英字・数字・空白・句読点も1文字が1単位です。なお、韓国語のハングルも同じで、ふつうの形なら1文字1単位です。

絵文字は違います。多くの絵文字は2単位で保存されるので、"😀".lengthは2です。肌の色がついた手の形のように、いくつかの部品を組み合わせて一つの形に見せる絵文字は、4かそれ以上になることもあります。文字列を[...s]のように展開するとコードポイント単位に分かれ、😀一つは1になりますが、組み合わせた絵文字はそれでも部品の数だけ出てきます。画面に見える形の単位で正確に数えるには、Intl.Segmenterのような別の道具が必要です。

日本語の文字もいつも安心とは限りません。同じ「が」でも、「か」と濁点を別々に保存した形(分解形、NFD)で入ってくると長さが2になります。一部のソフトからコピーしたファイル名がこの形で入ってくることがあり、見た目は同じ文字なのに===の比較がfalseになることもあります。そういうときはs.normalize("NFC")で一つの形にそろえてから比べます。

もう一つ、文字数とバイト数は別物です。ファイルやネットワークでよく使うUTF-8方式では、ひらがなや漢字1文字はふつう3バイトです。「最大90バイト」のようにバイトで制限する入力欄があるなら、lengthで数えてはいけません。まとめると、日本語中心のふつうの文章ならlengthで数えて構いませんが、絵文字・分解形の文字・バイト制限が絡むときは、まず何を数えるのかを決める必要があります。

この練習場で実際に実行した結果
console.log("日本".length, "こんにちは".length);
console.log("😀".length, [..."😀"].length);
console.log("👍🏽".length, [..."👍🏽"].length);
出力
2 5
2 1
4 2
分解形の文字とUTF-8のバイト数
const a = "が";
const b = a.normalize("NFD");
console.log(a.length, b.length);
console.log(a === b);
console.log(a === b.normalize("NFC"));
const bytes = new TextEncoder().encode("日本");
console.log(bytes.length);
出力
1 2
false
true
6
何を数えるかで変わる「文字数」(この練習場で確かめた値)
文字列`length``[...s].length`UTF-8バイト
"日本"226
"abc"333
"😀"214
"👍🏽"428

3.切り出しと検索 — `slice`、`indexOf`、`includes`

s.slice(開始, 終了)は、開始番号から終了番号の「一つ手前」までを切り出して新しい文字列として返します。終了を省くと最後まで切り出し、負の数を渡すと後ろから数えます。つまりs.slice(0, 3)は先頭の3文字、s.slice(-4)は末尾の4文字です。終了番号を含まないという決まりは、第4講の繰り返しでi < nと書いたのと同じ感覚で、長さは「終了 − 開始」ですぐ計算できます。

検索には主に三つを使います。s.indexOf("探す語")は最初に現れる位置の番号を、見つからなければ-1を返します。位置はいらず、あるかどうかだけ知りたいなら、s.includes("探す語")がtrue/falseを返すので、第3講の条件分岐にそのまま入れやすくなります。startsWithとendsWithは、先頭や末尾がその語で始まる・終わるかを調べます。ファイル名が".csv"で終わるかを確かめる、といった使い方です。

indexOfの結果をそのまま条件に使うと落とし穴にはまります。探す語が先頭にあると結果は0ですが、第3講で見たように0はfalsyなので、if (s.indexOf("x"))は「先頭にあるとき」に偽になってしまいます。あるかどうかだけを見るならincludesを使い、indexOfを使うなら!== -1と比べてください。また、これらのメソッドは大文字と小文字を区別するので、"Apple".includes("apple")はfalseです。大文字小文字を無視したいときは、両方をtoLowerCase()でそろえてから探します。

終了番号は含まれない
const phone = "090-1234-5678";
console.log(phone.slice(0, 3));
console.log(phone.slice(-4));
console.log(phone.slice(4, 8));
出力
090
5678
1234
const file = "2026_売上記録.csv";
console.log(file.indexOf("_"));
console.log(file.indexOf("x"));
console.log(file.includes("売上"));
console.log(file.endsWith(".csv"));
console.log("Apple".includes("apple"));
const t = "Apple".toLowerCase();
console.log(t.includes("apple"));
出力
4
-1
true
true
false
true
例題例のメールアドレス"[email protected]"から、@の前のユーザー名だけを取り出すにはどうすればよいでしょうか。
const email = "[email protected]";
const at = email.indexOf("@");
if (at === -1) {
  console.log("メールの形式ではない");
} else {
  console.log(email.slice(0, at));
}
出力
kenta
  1. ステップ1 @の位置を探します。indexOf("@")は5です(k-e-n-t-aが0〜4番)。
  2. ステップ2 0番から5番の「一つ手前」まで切り出せばよいので、slice(0, 5)です。長さは5 − 0 = 5文字です。
  3. ステップ3 @のないアドレスが来るとindexOfが-1になり、slice(0, -1)は最後の1文字を除いたおかしな値になります。そこで、先に-1かどうかを確かめる条件を置きます。
答えemail.slice(0, email.indexOf("@")) — ただし、@がない場合を先に除いておく必要があります。

4.分ける・つなぐ・整える — `split`、`join`、`trim`

テキスト処理の半分はsplitとjoinです。s.split(",")はカンマを区切りにして文字列を切り分け、配列として返します。arr.join("-")は逆に、配列を一つの文字列につなぎます。文字列を配列に変えれば、第6講で学んだlength・map・filterがすべて使えるので、「分ける → 配列として処理 → またつなぐ」が基本の流れになります。第9講の自動化の例も、この流れの上に成り立っています。

splitには知っておきたい端のケースがあります。空文字列""を分けると、空の配列ではなく、空文字列を一つ含んだ配列[ '' ]になります。カンマが二つ続くと、その間に空文字列ができます。そのため「単語がいくつあるか」をsplit(" ").lengthで数えると、空の入力が1個になったり、空白が重なったところで数が増えたりします。境界値を必ず一緒に試すべき理由です。

trim()は前後の空白・タブ・改行を取り除きます(途中の空白はそのまま)。人が入力した値は前後に空白がついてくることが多いので、比較や保存の前にtrim()を通す習慣をつけるとよいでしょう。toUpperCase()・toLowerCase()は英字の大文字・小文字を変え、ひらがなや漢字のように大文字小文字のない文字はそのままにします。padStart(3, "0")は長さが3になるまで先頭を"0"で埋め、"7"を"007"にします。番号や時刻を桁をそろえて表示するときに使います。

分ければ配列、つなげば文字列
const line = "健太,りんご,3";
const cols = line.split(",");
console.log(cols);
console.log(cols.length, cols[1]);
console.log(cols.join(" / "));
出力
[ '健太', 'りんご', '3' ]
3 りんご
健太 / りんご / 3
空文字列を分けると[ '' ]
console.log("".split(","));
console.log("a,,b".split(","));
console.log("  Hi  ".trim().toUpperCase());
console.log("7".padStart(3, "0"));
console.log("あいう".toUpperCase());
出力
[ '' ]
[ 'a', '', 'b' ]
HI
007
あいう
  • split("")(空文字列を区切りにする)は1単位ずつ分けます。絵文字が混じると部品が壊れることがあるので、文字単位で分けるときは[...s]のほうが安全です。
  • split・trim・toUpperCaseはどれも新しい値を返します。元の文字列はそのままです。
  • 行単位で分けるときはsplit("\n")を使います。Windowsで作ったファイルは行末に見えない文字がもう一つついていることがあるので、分けたあとで各行をtrim()するほうが安全です。

5.置き換え — `replace`と`replaceAll`

s.replace("探す語", "置き換える語")は、最初に現れる一つだけを置き換えます。すべて置き換えるにはreplaceAllを使います。この違いのせいで「なぜ最初の一つしか変わらないのか」ということがよく起きるので、置き換えのコードを書いたら、同じ語が二回以上出てくる入力で必ず試してください。どちらのメソッドも元の文字列はそのままにして、置き換えた新しい文字列を返します。

置き換えは「削除」にも使えます。置き換える語に空文字列""を渡すと、見つかった部分が消えます。たとえば"090-1234-5678".replaceAll("-", "")は"09012345678"です。逆に、数字だけの電話番号にハイフンを入れたいなら、sliceで切り出してjoinでつなぐほうがすっきりします。どんな作業でも元のデータはそのままにして新しい値を作るので、処理の前後を並べて比べやすくなります。

replaceは最初の一つだけ
const s = "りんご-なし-りんご";
console.log(s.replace("りんご", "みかん"));
console.log(s.replaceAll("りんご", "みかん"));
console.log(s);
出力
みかん-なし-りんご
みかん-なし-みかん
りんご-なし-りんご
消してから、またつなぐ
const raw = "090-1234-5678";
const digits = raw.replaceAll("-", "");
console.log(digits);
const parts = [
  digits.slice(0, 3),
  digits.slice(3, 7),
  digits.slice(7),
];
console.log(parts.join("-"));
出力
09012345678
090-1234-5678
置き換え前後のテキストが長いと、目で比べるのは大変です。テキスト比較ツールに二つの結果を貼り付ければ変わった箇所だけが表示されるので、意図しないところまで変わっていないかをすぐ確かめられます。

6.正規表現の入り口 — 形で探す

ここまでは「この語そのもの」を探してきました。「数字が続いている部分」「空白がいくつも続いているところ」のように形で探したいときに使うのが正規表現です。JavaScriptでは二つの/の間にパターンを書き、後ろにつけるgは「最初の一つではなく全部」という意味です。正規表現はそれ自体が小さな言語なので、ここではよく使ういくつかを読める程度に見ておきます。

\dは数字1文字、\sは空白1文字(空白・タブ・改行)で、後ろにつく+は「一つ以上続いた」という意味です。つまり/\d+/gは「続いた数字のかたまり全部」、/\s+/gは「続いた空白全部」です。matchは見つかったかたまりを配列で返し、一つもなければ空の配列ではなくnullを返す点に注意してください。testは見つかったかどうかだけをtrue/falseで教えてくれます。

正規表現は強力ですが読みにくく、少し間違えるだけで思わぬところに当たったり、当たるべきところが漏れたりします。原則は二つです。まず、includes・split・replaceAllでできることはそれで済ませ、正規表現はどうしても必要なときだけ使います。そして、AIが作ってくれた正規表現でも自分で書いたものでも、当たるべき例と当たってはいけない例をいくつも入れて、正規表現テスターで確かめてから使います。

matchは見つからなければnull
const memo = "注文 3個、返品 12個";
console.log(memo.match(/\d+/g));
console.log("数字なし".match(/\d+/g));
console.log(/\d/.test(memo));
出力
[ '3', '12' ]
null
true
空白のかたまりを一つに、数字を伏せる
const messy = "  りんご   なし  みかん ";
const clean = messy.trim().replace(/\s+/g, " ");
console.log("[" + clean + "]");
const masked = "090-1234-5678".replace(/\d/g, "*");
console.log(masked);
出力
[りんご なし みかん]
***-****-****
よく見る正規表現の部品
部品意味例
\d数字1文字/\d+/g → 続いた数字全部
\s空白1文字(タブ・改行を含む)/\s+/g → 続いた空白全部
+直前のものが一つ以上/a+/ → a、aa、aaa
^、$文字列の先頭、末尾/^\d+$/ → 全部数字のときだけ
g全部探す(つけなければ最初の一つ)s.replace(/\d/g, "*")

📌 要点まとめ

  • 文字列は変更できない値なので、すべてのメソッドが新しい文字列を返す — 結果は変数で受け取る
  • slice(開始, 終了)は終了を含まず、indexOfは見つからなければ-1 — あるかどうかだけ見るならincludes
  • 「分ける(split)→ 配列として処理 → つなぐ(join)」がテキスト処理の基本の流れで、空文字列を分けると[ '' ]になる
  • replaceは最初の一つだけ、replaceAllは全部を置き換える
  • lengthはUTF-16の単位数 — ひらがなや漢字は1文字1だが、絵文字は2以上、分解形の「が」は2、UTF-8のバイト数はまた別

✍️ 確認問題

まず自分で解いてから「解答と解説」を開いてください。

Q1. const s = "abc"; s.toUpperCase(); console.log(s);の出力は?

⭕ 正解です

❌ もう一度考えてみましょう

解答と解説
正解 ① abc

toUpperCase()は新しい文字列"ABC"を返すだけで、sは変えません。結果はs = s.toUpperCase()のように受け取る必要があります。

Q2. "090-1234-5678".slice(-4)の値は?

⭕ 正解です

❌ もう一度考えてみましょう

解答と解説
正解 ① "5678"

負の数は後ろから数えます。-4から最後までなので、末尾の4文字"5678"です。

Q3. if (s.indexOf("あ")) { ... }が意図と違う動きをするのはどんなときですか。

⭕ 正解です

❌ もう一度考えてみましょう

解答と解説
正解 ① "あ"が先頭にあるとき

先頭にあると結果は0で、0はfalsyなので条件が偽になります。逆に、見つからないときの-1はtruthyなので真になります。includesを使うか、!== -1と比べます。

Q4. "りんご,なし,りんご".replace("りんご", "みかん")の結果は?

⭕ 正解です

❌ もう一度考えてみましょう

解答と解説
正解 ① "みかん,なし,りんご"

replaceに文字列を渡すと、最初に現れる一つだけを置き換えます。全部置き換えるならreplaceAllです。

Q5. "日本".lengthが2なのに、"😀".lengthも2になる理由を一文で説明してください。

解答と解説
解答例 lengthは見た目の文字数ではなくUTF-16のコード単位の数を数えるもので、ひらがなや漢字は1文字が1単位なのに対し、この絵文字は2単位で保存されているからです。

[..."😀"].lengthは1です。部品を組み合わせた絵文字はこう展開しても2以上になることがあり、見た目の形の単位で数えるにはIntl.Segmenterのような道具が必要です。

🧪 コード実習

テキストを切り出し、数え、整える関数を自分で作ってみます。空文字列のような境界値のテストも一緒に採点されます。

コードはこのブラウザの中の隔離された場所でだけ実行され、サーバーには送られません。ネットワークには接続できず、2秒を超えると止まります。書き換えたコードはこのブラウザにだけ保存されます。Ctrl+Enter(⌘+Enter)で実行、Tabキーは空白2つ(Escの後のTabで次へ移動)。

JavaScriptが無効なので実行はできませんが、問題・最初のコード・自動チェックの内容・解答例はそのまま読めます。

1名前を伏せ字にする

名前nameを受け取り、最初の1文字だけを残して残りの文字をすべて*に変えた文字列を返す関数maskName(name)を作ってください。例:"田中愛" → "田**"。1文字の名前はそのまま、空文字列なら空文字列を返します。

自動チェック
  • maskName("田中愛")期待値 "田**"
  • maskName("佐藤花子")期待値 "佐***"
  • maskName("林")期待値 "林"
  • maskName("")期待値 ""
💡 ヒント

空文字列だとname.length - 1が-1になり、"*".repeat(-1)はRangeErrorを出します。Math.max(…, 0)か条件分岐で防いでください。

解答例を見る
function maskName(name) {
  const first = name.slice(0, 1);
  const rest = name.length - 1;
  return first + "*".repeat(Math.max(rest, 0));
}

2単語が何回出てくるか

文textの中に単語wordが何回出てくるかを数えて数値で返す関数countWord(text, word)を作ってください。大文字と小文字は区別します。例:countWord("りんご なし りんご", "りんご") → 2。textが空文字列なら0です。ほかの語の中に含まれていても数えます("りんごの木"の中の"りんご"も1回)。wordは空文字列ではないものとします。

自動チェック
  • countWord("りんご なし りんご", "りんご")期待値 2
  • countWord("りんご なし りんご", "みかん")期待値 0
  • countWord("a-a-a-a", "a")期待値 4
  • countWord("Apple apple", "apple")期待値 1
  • countWord("", "りんご")期待値 0
💡 ヒント

"a-a".split("a")は[ '', '-', '' ]のように前後に空のかけらができますが、個数は「出てきた回数 + 1」です。

解答例を見る
function countWord(text, word) {
  return text.split(word).length - 1;
}

3空白を整える

文字列sの前後の空白を取り除き、途中でいくつも続いている空白を一つにまとめた文字列を返す関数cleanSpaces(s)を作ってください。例:" りんご なし " → "りんご なし"。空白だけの文字列は空文字列になります。

自動チェック
  • cleanSpaces(" りんご なし ")期待値 "りんご なし"
  • cleanSpaces("a b c")期待値 "a b c"
  • cleanSpaces("もう きれい")期待値 "もう きれい"
  • cleanSpaces(" ")期待値 ""
  • cleanSpaces("")期待値 ""
💡 ヒント

split(/\s+/)で分けてからjoin(" ")でつなぐ方法でもできます。ただし、その場合も先にtrim()しないと前後に空のかけらができます。

解答例を見る
function cleanSpaces(s) {
  return s.trim().replace(/\s+/g, " ");
}

4タイトルをアドレス用の名前に

英語のタイトルtitleを受け取り、アドレス(URL)に使いやすい名前(スラッグ)に変える関数toSlug(title)を作ってください。決まり:前後の空白を取り除く → すべて小文字にする → 続いた空白はハイフン-一つにする。例:" Hello World " → "hello-world"。

自動チェック
  • toSlug(" Hello World ")期待値 "hello-world"
  • toSlug("My First Post")期待値 "my-first-post"
  • toSlug("JS")期待値 "js"
  • toSlug("a b c d")期待値 "a-b-c-d"
  • toSlug("")期待値 ""
💡 ヒント

最初のコードのreplace(" ", "-")は最初の空白一つしか置き換えません。メソッドはドット(.)でつないで呼べますが、順番が大切です。

解答例を見る
function toSlug(title) {
  return title
    .trim()
    .toLowerCase()
    .replace(/\s+/g, "-");
}

🤖 AIにはこう聞いてみましょう

コピーして [ ] の部分を自分の状況に書き換えて使います。答えはそのまま信じず、本文と比べて確かめましょう。

テキストを整えるコードを作りたいとき

JavaScriptで文字列を整える関数を作りたいです。入力例:[入力例を2〜3個]。望む結果:[それぞれの期待する結果]。正規表現がどうしても必要でなければ`split`・`replaceAll`・`trim`のような基本メソッドで書いてください。空文字列や空白だけの入力でどうなるかも教えてください。

AIがくれた正規表現の意味がわからないとき

次の正規表現を部品ごとに分けて、それぞれの意味を説明してください:[正規表現]。この正規表現に当てはまる例を3つと、当てはまりそうで当てはまらない例を3つ挙げてください。私が探したいのは[探したい形]ですが、この正規表現がそれと違う動きをする場合があれば指摘してください。

文字数が予想と違うとき

JavaScriptで[文字列の例]の`length`が[出た値]になります。画面に見える文字数は[見える数]です。なぜ違うのかをUTF-16・コードポイント・バイトの観点から説明し、私が数えたいのが[文字数/バイト数のどちらか]の場合に使える方法を教えてください。

🧰 関連ツール

この講義で扱ったJSON・正規表現・テキストを、その場で確かめたり整えたりできるツールです。本物の個人情報やパスワードは貼り付けないでください。

参考
  • MDN Web Docs — String(JavaScriptリファレンス)
  • MDN Web Docs — 正規表現(Regular expressions)ガイド
  • MDN Web Docs — String.prototype.normalize()、Intl.Segmenter
  • ECMAScript言語仕様(ECMA-262)— Stringオブジェクト

学習目標をすべて達成できたら完了を押しましょう。

💻 コーディングの基礎

  1. 1プログラムとは — 仕事を順番・条件・繰り返しに分ける考え方
  2. 2値・変数・データ型 — 名札を付けて値を扱う
  3. 3条件分岐 — 状況によって違う道を進む
  4. 4繰り返し — 同じことを正確に何度もさせる
  5. 5関数 — 名前を付けた小さな機械に分ける
  6. 6配列とオブジェクト — データを入れて扱う
  7. 7文字列とテキスト処理 — 文字を切り出し、探し、置き換える
  8. 8デバッグとエラーの読み方 — 赤い文字を手がかりに変える
  9. 9かんたんな自動化の例 — 表の計算とテキストの整理をコードで
  10. 10AIが書いたコードを読んで検証する — テスト・セキュリティ・ライセンス・個人情報
📚 あわせて読みたい
📊表計算の作業を人工知能で手伝わせる、数式は必ず検証→ 💻人工知能のコーディング支援の使い方と危険、セキュリティとライセンス→ 💬外国語の会話練習相手としてAIを使う→ ⚙️業務自動化のアイデア探し、AIに任せる仕事の選び方→
← AI時代の基礎学力講座