はじめに (対象読者・この記事でわかること)

この記事は、JavaScriptの基本的な文法を理解している方を対象にしています。特に、文字列操作や正規表現に興味がある方、フォーム入力のバリデーションやデータ解析の実装を行いたい方に向けています。

この記事を読むことで、JavaScriptでかっこ内の文字を抽出する方法、特に抽出結果が空の場合にundefinedではなく空文字を返す実装方法が学べます。正規表現を使った文字列操作の基本から実践的な関数の作成まで、具体的なコード例を交えて解説します。これにより、より堅牢な文字列処理を実装できるようになります。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - JavaScriptの基本的な文法(変数、関数、条件分岐など) - 正規表現の基本的な知識(あればですが、記事内で解説します)

JavaScriptでかっこ内文字抽出の必要性

Web開発において、ユーザーからの入力値や外部APIから取得したデータから特定のパターンを抽出する必要がある場面は頻繁にあります。特に、かっこ内の文字を抽出する処理は、以下のようなシナリオで役立ちます。

  • ユーザー名やタイトルからニックネーム部分を抽出する
  • メールアドレスからドメイン部分を分離する
  • 商品コードやバーコードから特定の情報を抽出する
  • ログファイルからエラーメッセージを抽出する

これらの処理を実装する際、特に重要となるのが「抽出結果が空の場合の処理」です。多くの開発者が初めて実装する際、抽出結果が空の場合にundefinedやnullを返してしまい、後続の処理でエラーが発生することがあります。本記事では、この問題を解決する方法を具体的に解説します。

かっこ内の文字抽出と空文字返却の実装方法

ステップ1: 基本的なかっこ内文字の抽出

まずは、正規表現を使ってかっこ内の文字を抽出する基本的な方法を見ていきましょう。JavaScriptには文字列操作に便利なメソッドが多数用意されていますが、特にmatch()メソッドと正規表現を組み合わせることで簡単に実装できます。

Javascript
function extractTextInParentheses(str) { // 正規表現でかっこ内の文字を抽出 const match = str.match(/\((.*?)\)/); // match()は一致した場合に配列を返し、不一致の場合はnullを返す return match ? match[1] : ''; } // 使用例 console.log(extractTextInParentheses("こんにちは(世界)")); // "世界" console.log(extractTextInParentheses("こんにちは")); // ""

このコードのポイントは、正規表現\((.*?)\)です。これは「左かっこから始まり、最短一致で任意の文字列を0回以上繰り返し、右かっこで終わる」というパターンに一致します。match()メソッドは一致した場合に配列を返し、不一致の場合はnullを返すため、三項演算子を使ってnullの場合は空文字を返すようにしています。

ステップ2: 複数の一致がある場合の処理

文字列内に複数のかっこが含まれる場合、上記のコードは最初に一致した部分のみを返します。すべてのかっこ内の文字を抽出したい場合は、matchAll()メソッドを使います。

Javascript
function extractAllTextInParentheses(str) { // matchAllですべてのかっこ内の文字を抽出 const matches = [...str.matchAll(/\((.*?)\)/g)]; // マッチしたテキストを配列として返す return matches.map(match => match[1]); } // 使用例 console.log(extractAllTextInParentheses("A(1)B(2)C(3)")); // ["1", "2", "3"] console.log(extractAllTextInParentheses("Hello")); // []

matchAll()メソッドはイテレータを返すため、スプレッド構文[... ]で配列に変換しています。また、グローバルフラグgを指定することで、文字列全体に対して検索を行います。

ステップ3: ネストされたかっこに対応する方法

より複雑なケースとして、かっこがネストされている場合の処理も考えてみましょう。この場合、単純な正規表現では対応できないため、再帰的なアプローチやスタックを使った方法が必要です。

Javascript
function extractNestedTextInParentheses(str) { const result = []; let stack = []; let start = -1; for (let i = 0; i < str.length; i++) { if (str[i] === '(') { stack.push(i); } else if (str[i] === ')' && stack.length > 0) { start = stack.pop(); if (stack.length === 0) { result.push(str.substring(start + 1, i)); } } } return result; } // 使用例 console.log(extractNestedTextInParentheses("A(1(B(2)C)3)D")); // ["1(B(2)C", "2"]

この実装では、スタックを使ってネストされたかっこの対応関係を追跡しています。左かっこが現れるたびに位置をスタックに保存し、右かっこが現れたらスタックから位置を取り出して対応関係を確認します。

ステップ4: より堅牢な関数の実装

実際の開発では、様々なケースに対応した堅牢な関数が必要になります。以下に、様々な状況に対応した実装例を示します。

Javascript
/** * かっこ内の文字を抽出する関数 * @param {string} str - 検索対象の文字列 * @param {boolean} [returnFirst=true] - 最初の一致のみを返すかどうか * @returns {string|string[]} - 一致した文字列または配列、不一致の場合は空文字または空配列 */ function extractTextInParentheses(str, returnFirst = true) { if (typeof str !== 'string') { return returnFirst ? '' : []; } const regex = /\((.*?)\)/g; const matches = [...str.matchAll(regex)]; if (matches.length === 0) { return returnFirst ? '' : []; } if (returnFirst) { return matches[0][1]; } else { return matches.map(match => match[1]); } } // 使用例 console.log(extractTextInParentheses("テスト(内容)です")); // "内容" console.log(extractTextInParentheses("A(1)B(2)C(3)", false)); // ["1", "2", "3"] console.log(extractTextInParentheses("ないよ")); // "" console.log(extractTextInParentheses(12345)); // ""

この関数は、以下のような特徴を持っています: - 文字列以外の入力に対しても安全に動作する - 最初の一致のみを返すか、すべての一致を返すかをオプションで指定可能 - 一致しない場合にundefinedやnullではなく空文字または空配列を返す - JSDocを使って関数の使い方を明示している

ステップ5: 実践的な応用例

この関数を実際の開発でどのように活用できるか、いくつかの応用例を見ていきましょう。

例1: フォーム入力からの情報抽出

ユーザーが「名前(ニックネーム)」のような形式で入力した文字列から、名前とニックネームを分離する例です。

Javascript
function parseUserName(input) { const nameMatch = input.match(/^([^(]+)(?:\((.*)\))?$/); return { fullName: input, firstName: nameMatch[1]?.trim() || '', nickName: nameMatch[2]?.trim() || '' }; } // 使用例 console.log(parseUserName("山田 太郎(たろー)")); // { fullName: "山田 太郎(たろー)", firstName: "山田 太郎", nickName: "たろー" } console.log(parseUserName("佐藤 花子")); // { fullName: "佐藤 花子", firstName: "佐藤 花子", nickName: "" }

例2: 商品コードからの情報抽出

商品コード「カテゴリ-サブカテゴリ(バリエーション)」のような形式から情報を抽出する例です。

Javascript
function parseProductCode(code) { const parts = code.split('-'); const category = parts[0] || ''; const subCategoryMatch = parts[1]?.match(/^(.*)\((.*)\)$/); return { category, subCategory: subCategoryMatch?.[1] || '', variation: subCategoryMatch?.[2] || '' }; } // 使用例 console.log(parseProductCode("電化製品-スマートフォン(赤)")); // { category: "電化製品", subCategory: "スマートフォン", variation: "赤" } console.log(parseProductCode "食品-飲料")); // { category: "食品", subCategory: "飲料", variation: "" }

ハマった点やエラー解決

実装中によく遭遇する問題とその解決方法について解説します。

問題1: 正規表現の贪婪マッチによる意図しない結果

初学者が陥りやすいのが、正規表現の贪婪マッチ(greedy match)による問題です。

Javascript
// 誤った例 const str = "A(1)B(2)C(3)"; const match = str.match(/\((.*)\)/); console.log(match[1]); // "1)B(2)C" ではなく "1)B(2)C" になってしまう

解決策: 非贪婪マッチ(lazy match)を使って、最初に一致した部分のみを抽出します。

Javascript
// 正しい例 const str = "A(1)B(2)C(3)"; const match = str.match(/\((.*?)\)/); console.log(match[1]); // "1" が正しく抽出される

*の代わりに*?を使うことで、最短一致モードになります。

問題2: エスケープ文字の処理

かっこそのものを検索対象に含める場合、エスケープ処理が必要です。

Javascript
// 誤った例 const str = "価格は¥(1,000)です"; const match = str.match(/\((.*)\)/); console.log(match[1]); // 期待通りに抽出されない

解決策: かっこをエスケープします。

Javascript
// 正しい例 const str = "価格は¥(1,000)です"; const match = str.match(/\((.*)\)/); console.log(match[1]); // "1,000" が正しく抽出される

問題3: 改行文字を含む場合の処理

文字列に改行が含まれる場合、.は改行に一致しないため問題が発生します。

Javascript
// 誤った例 const str = "詳細:\n(重要事項)"; const match = str.match(/\((.*)\)/); console.log(match[1]); // undefined になる

解決策: ドットオールフラグ(sフラグ)を使うか、改行文字を明示的に許容します。

Javascript
// 解決策1: sフラグを使用 const str = "詳細:\n(重要事項)"; const match = str.match(/\((.*)\)/s); console.log(match[1]); // "\n重要事項" が抽出される // 解決策2: 改行文字を明示的に許容 const match2 = str.match(/\(([\s\S]*)\)/); console.log(match2[1]); // "\n重要事項" が抽出される

まとめ

本記事では、JavaScriptでかっこ内の文字を抽出する方法、特に抽出結果が空の場合に空文字を返す実装方法について解説しました。

  • 正規表現を使った基本的な抽出方法として、match()メソッドと非贪婪マッチの組み合わせを学びました
  • 複数の一致やネストされたかっこに対応するための高度なテクニックを理解しました
  • 堅牢な関数の実装では、型チェックやオプション指定、JSDocの活用など実践的なポイントを学びました
  • 実践的な応用例として、フォーム入力や商品コードの解析など具体的なユースケースを紹介しました
  • よくある問題とその解決策として、贪婪マッチ、エスケープ処理、改行文字の扱いなど注意点を確認しました

これらの知識を活用することで、より安全で堅牢な文字列処理を実装できるようになります。特に、抽出結果が空の場合にundefinedやnullではなく空文字を返す実装は、後続の処理でエラーを防ぐ上で重要です。

今後は、さらに高度な文字列処理やパターンマッチングについても記事にする予定ですので、お楽しみに。

参考資料

本記事を作成する際に参考にした情報源です。