JavaScriptで「特定の文字より後ろをまるごと消したい」という場面はよくあります。たとえばURLから?以降のクエリ文字列を取り除いたり、ファイル名から拡張子を落としたりするケースです。
基本は「indexOf()で目印の位置を調べ、その手前までを切り出す」という流れになります。ただし、目印の文字が見つからなかったときの挙動には注意が必要で、ここを見落とすと意図せず文字列が空になったり末尾が欠けたりします。この記事では substring() / slice() / split() / 正規表現の4通りを比較しながら、安全に書くためのポイントまで解説します。
indexOf() で位置を取得し、-1(見つからない)かどうかを必ずチェックしてから slice(0, index) で切り出すのが安全です。「目印を含めて消すか・残すか」で渡す位置が1つズレる点も押さえておきましょう。substring() で指定文字以降を削除する
substring(開始, 終了) は、開始位置から終了位置の手前までを取り出すメソッドです。目印の文字をindexOf() で検索し、その位置を終了として渡せば「目印以降」を削除できます。
const str = "abcdefg";
const index = str.indexOf("c"); // "c" の位置(=2)を取得
const result = str.substring(0, index); // 先頭から "c" の手前まで
console.log(result); // "ab"
substring() の詳しい挙動はsubstringメソッドで文字列の一部を切り取る方法で解説しています。目印の文字を残したいとき
「cそのものは残して、その後ろだけ消したい」場合は、終了位置を index + 1 にします。
const str = "abcdefg";
const index = str.indexOf("c");
console.log(str.substring(0, index)); // "ab" (c を含めず削除)
console.log(str.substring(0, index + 1)); // "abc" (c を残して削除)
slice() で指定文字以降を削除する
slice(開始, 終了) も基本の使い方は substring() と同じで、先頭から目印の手前までを切り出します。前から切り出す用途ではどちらを使っても結果は同じです。
const str = "abcdefg";
const index = str.indexOf("c");
const result = str.slice(0, index);
console.log(result); // "ab"
slice() は配列にも使えるメソッドです。詳しくはsliceメソッドで配列や文字列の一部を切り取る方法を参照してください。【最重要】目印が見つからないときの落とし穴
ここが実務で一番ハマるポイントです。indexOf() は目印が見つからないと -1 を返します。この -1 をそのまま substring() や slice() に渡すと、2つのメソッドで結果がまったく異なります。
const str = "abcdefg";
const index = str.indexOf("z"); // 存在しない → -1
// substring は負の値を 0 として扱う → substring(0, 0)
console.log(str.substring(0, index)); // ""(空文字になってしまう)
// slice は負の値を「末尾からの位置」として扱う → slice(0, 6)
console.log(str.slice(0, index)); // "abcdef"(末尾が1文字欠ける)
substring() は負の引数を 0 とみなすため全部消えて空文字に、slice() は -1 を「末尾から1文字目」と解釈するため末尾が削れてしまいます。見つからなかったときは「元の文字列をそのまま返す」のが正しい動作です。安全な書き方
必ず -1 を判定し、見つからない場合は元の文字列を返すようにします。関数にまとめておくと使い回しがきいて安全です。
function deleteAfter(str, target) {
const index = str.indexOf(target);
if (index === -1) return str; // 見つからなければそのまま返す
return str.slice(0, index);
}
console.log(deleteAfter("abcdefg", "c")); // "ab"
console.log(deleteAfter("abcdefg", "z")); // "abcdefg"(元のまま)
// 目印を残したいバージョン
function deleteAfterKeep(str, target) {
const index = str.indexOf(target);
if (index === -1) return str;
return str.slice(0, index + target.length); // 複数文字の目印にも対応
}
console.log(deleteAfterKeep("https://example.com/path", "/path")); // "https://example.com/path"
console.log(deleteAfterKeep("2024-01-15T10:00", "T")); // "2024-01-15T"
split() で手軽に削除する
「最初に出てきた目印より後ろを消す」だけなら、split() で分割して先頭の要素を取る書き方が簡潔です。
const str = "abcdefg";
const result = str.split("c")[0];
console.log(result); // "ab"
// 区切り文字が複数文字でもOK
console.log("name=value=extra".split("=")[0]); // "name"
split() は「分割されなかった元の文字列を1要素だけ持つ配列」を返すため、[0] は自動的に元の文字列になります。-1 判定を書かなくても安全に動くのがメリットです。split(目印)[0] が拾うのは「最初の目印より前」だけです。目印が複数回出てくる文字列で「最後の目印以降を消したい」場合には使えません(後述の lastIndexOf() を使います)。正規表現(replace)で削除する
パターンで柔軟に消したいときはreplace() と正規表現が便利です。「目印の文字から行末まで」を空文字に置換します。
const str = "abcdefg"; // "c" 以降をすべて削除(s フラグで改行も . にマッチさせる) console.log(str.replace(/c.*/s, "")); // "ab" // 古い実行環境(s フラグ非対応)では [\s\S] を使う console.log(str.replace(/c[\s\S]*/, "")); // "ab"
変数の文字を目印にする
目印を変数で渡したいときは RegExp を組み立てます。このとき目印に . や ? など正規表現の特殊文字が含まれていると誤動作するため、必ずエスケープします。
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function deleteAfterRegex(str, target) {
const re = new RegExp(escapeRegExp(target) + "[\\s\\S]*");
return str.replace(re, "");
}
console.log(deleteAfterRegex("price: 100.50 yen", ".")); // "price: 100"
console.log(deleteAfterRegex("a?b?c", "?")); // "a"
最後に出てくる目印以降を削除する
目印が複数回登場する文字列で「最後の目印より後ろを消したい」場合は、indexOf() の代わりに lastIndexOf() を使います。挙動は同じで、検索方向が末尾からになるだけです。
function deleteAfterLast(str, target) {
const index = str.lastIndexOf(target);
if (index === -1) return str;
return str.slice(0, index);
}
// "." が複数あっても最後の "." 以降だけ消える
console.log(deleteAfterLast("archive.tar.gz", ".")); // "archive.tar"
console.log(deleteAfterLast("a.b.c.d", ".")); // "a.b.c"
よくある実用例
ファイル名から拡張子を削除する
拡張子は「最後のドット以降」なので lastIndexOf(".") を使います。先頭がドットで始まる隠しファイル(.gitignore など)を誤って空にしないよう index > 0 で判定すると安全です。
function removeExtension(filename) {
const index = filename.lastIndexOf(".");
return index > 0 ? filename.slice(0, index) : filename;
}
console.log(removeExtension("report.pdf")); // "report"
console.log(removeExtension("photo.final.jpg")); // "photo.final"
console.log(removeExtension(".gitignore")); // ".gitignore"(消えない)
URLからクエリ文字列・ハッシュを削除する
? や # 以降を消すなら split() が手軽です。目印が無いURLでもそのまま返るので安心です。
const url = "https://example.com/page?id=10&ref=top";
console.log(url.split("?")[0]); // "https://example.com/page"
console.log(url.split("#")[0]); // ハッシュが無いので URL 全体が返る
// ?と#の両方を一度に落とす
console.log(url.split(/[?#]/)[0]); // "https://example.com/page"
4つの方法の使い分け
| 方法 | 向いているケース | 見つからないとき |
|---|---|---|
slice() + indexOf |
位置を細かく制御したい・目印を残す/含めるを切り替えたい | 要 -1 判定 |
substring() + indexOf |
slice と同様(負の値の扱いだけ異なる) | 要 -1 判定 |
split() |
最初の目印以降をサッと消す・短く書きたい | 元の文字列が返る(安全) |
正規表現 replace() |
パターンや複数条件で柔軟に消したい | 元の文字列が返る(安全) |
split()、「含める/残す」の制御や最後の出現を扱うなら slice() + indexOf()/lastIndexOf()を選ぶと分かりやすいです。よくある質問(FAQ)
slice() は負の値を「末尾からの位置」として扱い、substring() は負の値を 0 として扱います。indexOf() が -1 を返したときに挙動が分かれる原因なので、いずれにせよ -1 判定は必須です。indexOf() で調べる必要はなく、slice(0, 文字数) または substring(0, 文字数) で先頭から指定文字数だけを残せます。末尾に「…」を付ける省略表示(truncate)にする場合は str.length と比較してから付与します。詳しくは先頭から指定した文字数の文字列を取得する方法を参照してください。slice(0, index) のように位置をそのまま渡すと、目印の文字は含まれず削除されます。目印を残したい場合は slice(0, index + 目印の長さ) のように位置を後ろにずらします。substr() は非推奨のため、新しいコードでは slice() か substring() を使うのがおすすめです。TextEncoder で Uint8Array に変換し、バイト数を確認しながら切り詰めて TextDecoder で文字列に戻します。日本語などのマルチバイト文字の境界を跨がないよう注意が必要で、TextDecoder の { stream: true } やfatal オプションで途中の壊れたバイト列を扱えます。まとめ
JavaScriptで指定した文字以降を削除する方法のポイントを整理します。
- 基本は
indexOf()で位置を取得し、slice(0, index)で手前までを切り出す - 見つからない(
-1)ときの判定を必ず入れる。入れないと空文字や末尾欠けのバグになる - 最初の目印以降をサッと消すなら
split(目印)[0]が安全で簡潔 - 最後の目印以降を消すなら
lastIndexOf()を使う(拡張子削除などに便利) - パターンで柔軟に消すなら正規表現。変数を目印にするときはエスケープを忘れない
文字列操作をさらに深めたい方は、split() で文字列を配列に変換する方法やreplace() で文字列を置換する方法、indexOf() で文字列を検索する方法もあわせて確認すると理解が深まります。

