JavaScriptでアルファベットの大文字ごとにスペースで区切る処理は、camelCase や PascalCase の変数名を人間が読みやすい形に変換するときに頻出します。
この記事では、replace() と正規表現を使った基本テクニックから、連続大文字(HTMLParser → HTML Parser)の正しい処理、snake_case / kebab-case への相互変換、lodash との比較、実務での活用例まで網羅的に解説します。
この記事で学べること
replace() + 正規表現で大文字前にスペースを挿入する基本
- camelCase・PascalCase をスペース区切りに変換する方法
- 連続大文字(略語)を正しく処理するテクニック
- snake_case / kebab-case への変換ユーティリティ
- lodash(
_.startCase)との比較
- 実務での活用例(UI表示・ログ整形・テスト出力)
- ブラウザ互換性とよくある落とし穴
replace() + 正規表現で大文字前にスペースを挿入する基本
最もシンプルなアプローチは、String.prototype.replace() に正規表現 /([A-Z])/g を渡し、大文字の前にスペースを挿入する方法です。
JavaScript – 大文字前にスペースを挿入(基本)
const str = "HelloWorldInJavaScript";
// 大文字 [A-Z] の前にスペースを挿入
const result = str.replace(/([A-Z])/g, ' $1').trim();
console.log(result);
// "Hello World In Java Script"
実行結果
"Hello World In Java Script"
仕組みの解説
| 要素 |
説明 |
/([A-Z])/g |
大文字 A〜Z にマッチ。() でキャプチャグループ化 |
' $1' |
スペース + キャプチャした大文字で置換 |
g フラグ |
文字列内のすべての大文字に適用(グローバル) |
.trim() |
先頭にスペースが入る場合を除去 |
注意:先頭が大文字の PascalCase(例: "HelloWorld")の場合、先頭にもスペースが挿入されるため、.trim() が必須です。
camelCase をスペース区切りに変換する
camelCase(先頭が小文字で始まる)をスペース区切りに変換する場合、先頭の単語もそのまま保持されます。
JavaScript – camelCase → スペース区切り
function camelToSpaces(str) {
return str.replace(/([A-Z])/g, ' $1').trim();
}
console.log(camelToSpaces("backgroundColor"));
console.log(camelToSpaces("fontSize"));
console.log(camelToSpaces("maxWidthValue"));
console.log(camelToSpaces("onClick"));
実行結果
"background Color"
"font Size"
"max Width Value"
"on Click"
camelCase の場合は先頭が小文字なので .trim() は不要ですが、PascalCase と統一的に扱うため付けておくと安全です。
PascalCase をスペース区切りに変換する
PascalCase(先頭も大文字)は React のコンポーネント名などで頻出します。先頭のスペースを除去するために .trim() を使うか、正規表現側で先頭文字を除外します。
JavaScript – PascalCase → スペース区切り(2パターン)
// パターン1: replace + trim(シンプル)
function pascalToSpaces(str) {
return str.replace(/([A-Z])/g, ' $1').trim();
}
// パターン2: 先頭以外の大文字にのみマッチ(trim 不要)
function pascalToSpacesV2(str) {
return str.replace(/(?<=[a-z])([A-Z])/g, ' $1');
}
console.log(pascalToSpaces("HelloWorld"));
console.log(pascalToSpacesV2("HelloWorld"));
console.log(pascalToSpaces("UserProfileSettings"));
console.log(pascalToSpacesV2("UserProfileSettings"));
実行結果
"Hello World"
"Hello World"
"User Profile Settings"
"User Profile Settings"
ポイント:パターン2の (?<=[a-z]) は「後読み(lookbehind)」で、小文字の後に続く大文字だけにマッチします。.trim() が不要になりコードが簡潔になります。
連続大文字(略語)を正しく処理する
実務では HTMLParser、XMLHttpRequest、getAPIResponse のように連続する大文字(略語)を含む文字列を扱うことがあります。単純な /([A-Z])/g では各文字が分割されてしまいます。
JavaScript – 単純な方法の問題点
// 単純な方法 → 略語がバラバラになる
const bad = "HTMLParser".replace(/([A-Z])/g, ' $1').trim();
console.log(bad);
// "H T M L Parser" ← 期待: "HTML Parser"
実行結果
"H T M L Parser" ← 期待した結果ではない!
この問題を解決するには、2つのパターンを組み合わせた正規表現を使います。
JavaScript – 連続大文字を正しく処理
function splitByUpperCase(str) {
return str
// パターン1: 連続大文字の後に小文字が続く境界
// "HTMLParser" → "HTML Parser"
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
// パターン2: 小文字/数字の後に大文字が続く境界
// "getAPI" → "get API"
.replace(/([a-z0-9])([A-Z])/g, '$1 $2');
}
console.log(splitByUpperCase("HTMLParser"));
console.log(splitByUpperCase("XMLHttpRequest"));
console.log(splitByUpperCase("getAPIResponse"));
console.log(splitByUpperCase("iPhone12ProMax"));
console.log(splitByUpperCase("parseJSON2XML"));
実行結果
"HTML Parser"
"XML Http Request"
"get API Response"
"i Phone12 Pro Max"
"parse JSON2 XML"
2つの正規表現パターンの解説
| パターン |
マッチ例 |
変換結果 |
/([A-Z]+)([A-Z][a-z])/g |
HTML + Parser |
HTML Parser |
/([a-z0-9])([A-Z])/g |
get + API |
get API |
ポイント:この「2段階 replace」パターンは、多くの OSS ライブラリ(lodash、change-case など)でも採用されている実績のあるアルゴリズムです。
数字を含む文字列の処理
実務では iPhone12ProMax や es2024Features のように数字を含む変数名も多くあります。数字とアルファベットの境界でも区切りたい場合は、パターンを追加します。
JavaScript – 数字を含む文字列の分割
function splitWords(str) {
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
.replace(/([a-z])([A-Z])/g, '$1 $2')
// 数字→文字 の境界
.replace(/([0-9])([a-zA-Z])/g, '$1 $2')
// 文字→数字 の境界
.replace(/([a-zA-Z])([0-9])/g, '$1 $2');
}
console.log(splitWords("iPhone12ProMax"));
console.log(splitWords("es2024Features"));
console.log(splitWords("html5Canvas2D"));
console.log(splitWords("parseJSON2XML"));
実行結果
"i Phone 12 Pro Max"
"es 2024 Features"
"html 5 Canvas 2 D"
"parse JSON 2 XML"
snake_case / kebab-case への変換
スペース区切りの応用として、camelCase → snake_case や camelCase → kebab-case への変換もよく使われます。スペース区切りの結果をベースに、区切り文字を変えるだけで実現できます。
JavaScript – 各種ケース変換ユーティリティ
// 単語分割のベース関数
function toWords(str) {
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
.replace(/[_\-]+/g, ' ')
.trim()
.split(/\s+/);
}
// camelCase → snake_case
function toSnakeCase(str) {
return toWords(str).map(w => w.toLowerCase()).join('_');
}
// camelCase → kebab-case
function toKebabCase(str) {
return toWords(str).map(w => w.toLowerCase()).join('-');
}
// camelCase → Title Case(先頭大文字スペース区切り)
function toTitleCase(str) {
return toWords(str)
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join(' ');
}
// camelCase → CONSTANT_CASE
function toConstantCase(str) {
return toWords(str).map(w => w.toUpperCase()).join('_');
}
const input = "backgroundColor";
console.log(toSnakeCase(input));
console.log(toKebabCase(input));
console.log(toTitleCase(input));
console.log(toConstantCase(input));
実行結果
"background_color"
"background-color"
"Background Color"
"BACKGROUND_COLOR"
逆変換 ― snake_case / kebab-case → camelCase
大文字でスペース区切りにする処理の逆、つまり snake_case や kebab-case を camelCase / PascalCase に戻す変換も覚えておくと便利です。
JavaScript – snake_case / kebab-case → camelCase
// snake_case / kebab-case → camelCase
function toCamelCase(str) {
return str
.toLowerCase()
.replace(/[_\-\s]+(.)/g, (_, c) => c.toUpperCase());
}
// snake_case / kebab-case → PascalCase
function toPascalCase(str) {
return str
.toLowerCase()
.replace(/(^|[_\-\s]+)(.)/g, (_, __, c) => c.toUpperCase());
}
console.log(toCamelCase("background_color"));
console.log(toCamelCase("font-size"));
console.log(toPascalCase("user_profile_settings"));
console.log(toPascalCase("my-component-name"));
実行結果
"backgroundColor"
"fontSize"
"UserProfileSettings"
"MyComponentName"
lodash(_.startCase / _.camelCase)との比較
ケース変換は lodash や change-case などのライブラリでも提供されています。自前実装とライブラリの違いを比較してみましょう。
JavaScript – lodash のケース変換メソッド
import _ from 'lodash';
// _.startCase: スペース区切り + 先頭大文字
_.startCase("backgroundColor"); // "Background Color"
_.startCase("HTMLParser"); // "HTML Parser"
// _.camelCase: camelCase に変換
_.camelCase("background-color"); // "backgroundColor"
// _.snakeCase: snake_case に変換
_.snakeCase("backgroundColor"); // "background_color"
// _.kebabCase: kebab-case に変換
_.kebabCase("backgroundColor"); // "background-color"
自前実装 vs ライブラリの比較
| 観点 |
自前実装 |
lodash |
change-case |
| バンドルサイズ |
0 KB(追加なし) |
~70 KB(全体)/ ~2 KB(個別) |
~1 KB |
| 連続大文字処理 |
自分で実装が必要 |
自動対応 |
自動対応 |
| Unicode 対応 |
自分で拡張が必要 |
対応済み |
対応済み |
| エッジケース |
テスト次第 |
豊富なテスト済み |
豊富なテスト済み |
| カスタマイズ性 |
完全に自由 |
限定的 |
プラグイン可能 |
| 推奨シーン |
シンプルな変換 |
既に lodash 導入済み |
ケース変換が主目的 |
ポイント:シンプルな変換なら自前実装で十分です。プロジェクトに lodash が既に導入されている場合は、_.startCase() を使うのが最も手軽です。バンドルサイズを気にする場合は lodash/startCase で個別インポートしましょう。
実務での活用例
大文字でのスペース区切り・ケース変換は、フロントエンド・バックエンド問わず多くの場面で活用されます。代表的なユースケースを見てみましょう。
例1: オブジェクトのキーをUI表示用に変換
API から受け取った JSON のキー(camelCase)を、画面上ではスペース区切りのラベルとして表示するケースです。
JavaScript – APIキーをUI表示用に変換
const user = {
firstName: "太郎",
lastName: "山田",
emailAddress: "taro@example.com",
phoneNumber: "090-1234-5678",
createdAt: "2024-01-15"
};
// キーをラベルに変換して表示
Object.entries(user).forEach(([key, value]) => {
const label = key
.replace(/([A-Z])/g, ' $1')
.replace(/^./, c => c.toUpperCase());
console.log(`${label}: ${value}`);
});
実行結果
First Name: 太郎
Last Name: 山田
Email Address: taro@example.com
Phone Number: 090-1234-5678
Created At: 2024-01-15
例2: React コンポーネント名からパンくずリストを生成
JavaScript – コンポーネント名からパンくず生成
function toBreadcrumb(componentName) {
return componentName
.replace(/([A-Z])/g, ' $1')
.trim()
.split(' ')
.join(' > ');
}
console.log(toBreadcrumb("UserProfileSettings"));
console.log(toBreadcrumb("AdminDashboardAnalytics"));
実行結果
"User > Profile > Settings"
"Admin > Dashboard > Analytics"
例3: CSS プロパティ名の相互変換
JavaScript – CSS プロパティの camelCase ↔ kebab-case
// camelCase → CSS kebab-case
function cssProp(jsProp) {
return jsProp.replace(/([A-Z])/g, '-$1').toLowerCase();
}
// CSS kebab-case → camelCase
function jsProp(cssPropName) {
return cssPropName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
}
console.log(cssProp("backgroundColor")); // "background-color"
console.log(cssProp("borderTopWidth")); // "border-top-width"
console.log(jsProp("margin-left")); // "marginLeft"
console.log(jsProp("font-size")); // "fontSize"
実行結果
"background-color"
"border-top-width"
"marginLeft"
"fontSize"
例4: テスト名の自動生成
JavaScript – 関数名からテストの説明文を生成
function describeFromName(fnName) {
const words = fnName
.replace(/([A-Z])/g, ' $1')
.toLowerCase()
.trim();
return `should ${words}`;
}
console.log(describeFromName("validateEmail"));
console.log(describeFromName("calculateTotalPrice"));
console.log(describeFromName("fetchUserData"));
実行結果
"should validate email"
"should calculate total price"
"should fetch user data"
汎用ケース変換クラス ― まとめて使えるユーティリティ
ここまでのテクニックをまとめて、1つのクラスとして再利用可能な形にしておきましょう。プロジェクト全体で統一的にケース変換を行うのに便利です。
JavaScript – CaseConverter クラス
class CaseConverter {
/** 文字列を単語配列に分解する(すべての変換の基礎) */
static toWords(str) {
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
.replace(/[_\-]+/g, ' ')
.trim()
.split(/\s+/)
.filter(Boolean);
}
static toCamel(str) {
const words = this.toWords(str);
return words[0].toLowerCase() +
words.slice(1).map(w =>
w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
).join('');
}
static toPascal(str) {
return this.toWords(str)
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join('');
}
static toSnake(str) {
return this.toWords(str).map(w => w.toLowerCase()).join('_');
}
static toKebab(str) {
return this.toWords(str).map(w => w.toLowerCase()).join('-');
}
static toTitle(str) {
return this.toWords(str)
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join(' ');
}
static toConstant(str) {
return this.toWords(str).map(w => w.toUpperCase()).join('_');
}
}
// 使用例
const input = "getUserAPIResponse";
console.log(CaseConverter.toWords(input));
console.log(CaseConverter.toSnake(input));
console.log(CaseConverter.toKebab(input));
console.log(CaseConverter.toTitle(input));
console.log(CaseConverter.toConstant(input));
実行結果
["get", "User", "API", "Response"]
"get_user_api_response"
"get-user-api-response"
"Get User Api Response"
"GET_USER_API_RESPONSE"
ブラウザ互換性
この記事で使用している JavaScript の機能について、ブラウザ対応状況を確認しておきましょう。
| 機能 |
Chrome |
Firefox |
Safari |
Edge |
IE |
String.replace() |
1+ |
1+ |
1+ |
12+ |
5.5+ |
| 正規表現(基本) |
1+ |
1+ |
1+ |
12+ |
5.5+ |
後読み (?<=...) |
62+ |
78+ |
16.4+ |
79+ |
非対応 |
| アロー関数 |
45+ |
22+ |
10+ |
12+ |
非対応 |
| テンプレートリテラル |
41+ |
34+ |
9+ |
12+ |
非対応 |
class 構文 |
49+ |
45+ |
9+ |
13+ |
非対応 |
注意:後読み(lookbehind)(?<=...) は Safari 16.4 未満では使えません。古いブラウザをサポートする必要がある場合は、.replace(/([A-Z])/g, ' $1').trim() パターンを使いましょう。
よくある落とし穴と対処法
大文字でのスペース区切り処理で遭遇しやすいミスと、その対処法をまとめます。
落とし穴1: 先頭の余分なスペース
JavaScript – trim() を忘れた場合
// NG: trim() なし → 先頭にスペースが入る
const bad = "HelloWorld".replace(/([A-Z])/g, ' $1');
console.log(`"${bad}"`); // " Hello World" ← 先頭にスペース!
// OK: trim() あり
const good = "HelloWorld".replace(/([A-Z])/g, ' $1').trim();
console.log(`"${good}"`); // "Hello World" ← 正しい
落とし穴2: 連続スペースの発生
JavaScript – 既にスペースを含む文字列の処理
// 入力に既にスペースやアンダースコアが含まれる場合
const mixed = "get_UserName";
// 対策: 区切り文字を統一してからスペース化
const result = mixed
.replace(/[_\-]+/g, ' ')
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
.replace(/\s+/g, ' ') // 連続スペースを1つに
.trim();
console.log(result); // "get User Name"
落とし穴3: 空文字列・null の処理
JavaScript – 安全な入力チェック
function safeSplitByUpperCase(str) {
// null / undefined / 非文字列のガード
if (typeof str !== 'string' || str.length === 0) {
return '';
}
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
.trim();
}
console.log(safeSplitByUpperCase("")); // ""
console.log(safeSplitByUpperCase(null)); // ""
console.log(safeSplitByUpperCase(undefined)); // ""
console.log(safeSplitByUpperCase("helloWorld")); // "hello World"
落とし穴4: g フラグの付け忘れ
JavaScript – g フラグの有無の違い
const str = "HelloWorldTest";
// NG: g フラグなし → 最初の1つしか置換されない
console.log(str.replace(/([A-Z])/, ' $1').trim());
// "Hello WorldTest" ← World と Test が分割されていない
// OK: g フラグあり → すべて置換される
console.log(str.replace(/([A-Z])/g, ' $1').trim());
// "Hello World Test" ← 正しい
ケース変換の早見表
各種ケース変換のパターンを一覧にまとめます。コピーして使ってください。
| 変換 |
入力例 |
出力例 |
コア処理 |
| 大文字スペース区切り |
helloWorld |
hello World |
replace(/([A-Z])/g, ' $1') |
| → snake_case |
helloWorld |
hello_world |
単語分割 + .join('_') |
| → kebab-case |
helloWorld |
hello-world |
単語分割 + .join('-') |
| → Title Case |
helloWorld |
Hello World |
単語分割 + 先頭大文字化 |
| → CONSTANT_CASE |
helloWorld |
HELLO_WORLD |
単語分割 + 全大文字 + .join('_') |
| snake → camelCase |
hello_world |
helloWorld |
replace(/[_-]+(.)/, toUpper) |
| CSS camel → kebab |
backgroundColor |
background-color |
replace(/([A-Z])/g, '-$1').toLowerCase() |
まとめ
JavaScriptでアルファベットの大文字ごとにスペースで区切る方法と、関連するケース変換テクニックを解説しました。
この記事のまとめ
replace(/([A-Z])/g, ' $1').trim() が最もシンプルな大文字スペース区切り
- 連続大文字(略語)には 2段階 replace パターンを使う
- snake_case / kebab-case への変換は、単語分割 +
join() で実現
- CSS プロパティの camelCase ↔ kebab-case 変換は実務で頻出
- lodash / change-case はエッジケースに強いが、シンプルな変換なら自前で十分
.trim() の付け忘れと g フラグの付け忘れに注意