文字列

【C#】正規表現で小数点を含む数値をスマートに判定する方法

小数点付きの数値をC#で判定したいとき、符号を考慮した最小パターン^[+-]?(\\d+\\.\\d*|\\.\\d+)$が実用的です。

整数部の省略や末尾ドットを許容しつつ余計な書式を弾けるため、Regex.IsMatchで入力チェックを手軽に強化できます。

目次から探す
  1. 正規表現の基本要素
  2. 小数点を含む数値に適したパターンの構造
  3. 実用パターン例と比較
  4. C#コードへの組み込み
  5. バリデーションユースケース
  6. よくある失敗パターン
  7. 拡張ニーズへの対応
  8. 多言語・カルチャー依存の注意点
  9. テストとデバッグの手法
  10. セキュリティ観点
  11. メンテナンス性を高める書き方
  12. 代替手段の検討
  13. まとめ

正規表現の基本要素

正規表現は文字列のパターンを表現するための強力なツールです。

C#で小数点を含む数値を判定する際にも、正規表現の基本的な要素を理解しておくことが重要です。

ここでは、正規表現の基礎となるメタ文字やエスケープ、文字クラス、量指定子について詳しく解説いたします。

メタ文字のおさらい

正規表現には特別な意味を持つ文字、いわゆる「メタ文字」があります。

これらは単なる文字としてではなく、パターンの構造や繰り返し、位置指定などを表現するために使われます。

主なメタ文字は以下の通りです。

メタ文字意味
.任意の1文字(改行を除く)a.c は “abc”, “a1c” にマッチ
^文字列の先頭^abc は “abcdef” にマッチ
$文字列の末尾xyz$ は “wxyz” にマッチ
*直前の文字の0回以上の繰り返しab*c は “ac”, “abc”, “abbc” にマッチ
+直前の文字の1回以上の繰り返しab+c は “abc”, “abbc” にマッチ、”ac” にはマッチしない
?直前の文字の0回または1回の出現colou?r は “color” と “colour” にマッチ
|OR(または)cat|dog は “cat” または “dog” にマッチ
()グループ化(abc)+ は “abc”, “abcabc” にマッチ
[]文字クラス(指定した文字のいずれか1文字)[abc] は “a”, “b”, “c” のいずれかにマッチ

これらのメタ文字を組み合わせることで、複雑なパターンを表現できます。

例えば、小数点を含む数値を表す場合、整数部や小数部の数字の繰り返しを+*で指定し、位置を^$で制御します。

エスケープが必要な文字

正規表現のメタ文字は特別な意味を持つため、文字通りにマッチさせたい場合は「エスケープ」が必要です。

エスケープとは、メタ文字の前にバックスラッシュ\を付けて、その文字を普通の文字として扱うことです。

小数点を含む数値の判定で特に重要なのは「.(ドット)」です。

ドットは「任意の1文字」を意味しますが、小数点としての「.」にマッチさせたい場合は\.と書きます。

主なエスケープが必要な文字は以下の通りです。

文字意味(メタ文字として)エスケープ例
.任意の1文字\.
^文字列の先頭\^
$文字列の末尾\$
*0回以上の繰り返し\*
+1回以上の繰り返し\+
?0回または1回の出現\?
|OR|
(グループ開始\(
)グループ終了\)
[文字クラス開始\##LATEX_START##
]文字クラス終了\##LATEX_END##
\エスケープ文字\\

例えば、小数点を含む数値の正規表現で「.」を使う場合は必ず\.と書きます。

これを忘れると、意図しない文字列にマッチしてしまうことがあります。

文字クラスと量指定子のポイント

文字クラス

文字クラスは[]で囲み、その中に指定した文字のいずれか1文字にマッチします。

例えば、[0-9]は数字の0から9のいずれか1文字にマッチします。

小数点を含む数値の判定では、数字を表すために\d(数字1文字)や[0-9]がよく使われます。

また、文字クラスの中でハイフン-を使うと範囲指定ができます。

例えば、[a-z]は小文字アルファベットのいずれか1文字にマッチします。

量指定子

量指定子は直前の文字やグループの繰り返し回数を指定します。

主な量指定子は以下の通りです。

量指定子意味
*0回以上の繰り返し\d* は数字0回以上
+1回以上の繰り返し\d+ は数字1回以上
?0回または1回の出現\d? は数字0回または1回
{n}ちょうどn回の繰り返し\d{3} は数字3回
{n,}n回以上の繰り返し\d{2,} は数字2回以上
{n,m}n回以上m回以下の繰り返し\d{1,3} は数字1~3回

小数点を含む数値の正規表現では、整数部や小数部の数字の繰り返しを指定するために、\d+\d*がよく使われます。

例えば、整数部は1桁以上の数字が必要なので\d+、小数部は0桁以上(省略可能)なので\d*と指定することがあります。

  • 文字クラスは特定の文字集合にマッチさせるために使います。数字なら\d[0-9]が代表的です
  • 量指定子は繰り返し回数を指定し、数値の桁数や小数部の有無を制御します
  • これらを組み合わせて、例えば「整数部は1桁以上の数字、小数点の後は0桁以上の数字」というパターンを作れます

これらの基本要素を理解しておくと、小数点を含む数値の正規表現を作成・修正する際に役立ちます。

小数点を含む数値に適したパターンの構造

必須要素とオプション要素の分類

小数点を含む数値の正規表現は、いくつかの要素に分けて考えると作りやすくなります。

主に「整数部」「小数点」「小数部」「符号」の4つの要素に分けられ、それぞれが必須かオプションかでパターンを組み立てます。

整数部 \d+

整数部は数字の連続で表されます。

正規表現では\dが数字1文字を表し、\d+は1回以上の数字の繰り返しを意味します。

整数部は通常、少なくとも1桁の数字が必要です。

  • 123 → マッチ
  • 0 → マッチ
  • 空文字 → マッチしない

整数部を必須にする場合は\d+を使います。

整数部が省略可能な場合は後述の「省略可能な整数部を許可する方法」で解説します。

小数点 .

小数点は数値の整数部と小数部を区切る記号です。

正規表現の.は任意の1文字を意味するため、小数点として使う場合はエスケープして\.と書きます。

小数点は小数部が存在する場合に必須です。

整数部のみの数値には含まれません。

  • 123.456 → 小数点あり
  • 123 → 小数点なし

小数点の有無はパターンの分岐で制御することが多いです。

小数部 \d*

小数部は小数点の後に続く数字の部分です。

\d*は数字の0回以上の繰り返しを意味し、小数部が省略可能な場合に使います。

  • 123.456 → 小数部は456(3桁)
  • 123. → 小数部は空(0桁)
  • .456 → 小数部は456(3桁)

小数部を必須にしたい場合は\d+を使いますが、一般的には小数点の後に数字がなくても許容するケースが多いです。

符号 + と –

数値の先頭に付く符号は、正の数か負の数かを示します。

符号はオプションであり、+または-のいずれか1文字が付く場合があります。

正規表現では[+-]?と書くことで、+または-が0回または1回出現することを表します。

  • +123.45 → 符号あり
  • -0.99 → 符号あり
  • 123.45 → 符号なし

符号を必須にすることはほとんどありません。

省略可能な整数部を許可する方法

整数部を省略可能にする場合、例えば.456のように小数点から始まる数値を許容したいときは、整数部の部分をオプションにします。

具体的には、整数部を\d*(0回以上の数字)にするか、整数部と小数部の組み合わせを分けてパターンを作ります。

代表的なパターンは以下のようになります。

  • \d+\.\d*:整数部が1桁以上、小数点、小数部は0桁以上
  • \.\d+:整数部なし、小数点、小数部は1桁以上

これらを|(OR)でつなげて、整数部がある場合とない場合の両方を許容します。

^[+-]?(\d+\.\d*|\.\d+)$ このパターンは、

  • 123.456(整数部あり)
  • .456(整数部なし)

の両方にマッチします。

整数部を\d*にしてしまうと、.だけの文字列にもマッチしてしまうため注意が必要です。

必ず小数部に1桁以上の数字を要求するか、整数部がある場合は小数部を0桁以上にするなどの工夫が必要です。

先頭ゼロを許容・拒否する選択

数値の整数部における先頭ゼロの扱いは、用途によって異なります。

例えば、0123のような先頭ゼロを許容するかどうかは要件次第です。

先頭ゼロを許容する場合

特に制限を設けず、\d+で整数部を表現すれば、00123のような数字もマッチします。

  • 0123.45 → マッチ
  • 0.123 → マッチ

先頭ゼロを拒否する場合

先頭ゼロを拒否したい場合は、整数部のパターンを工夫します。

例えば、整数部が1桁の0か、先頭が1~9の数字で始まる複数桁の数字に分けて書きます。

パターン例:

(0|[1-9]\d*)

  • 0:整数部が0のみ
  • [1-9]\d*:先頭が1~9で、続く数字は0回以上

これにより、0123のような先頭ゼロの複数桁はマッチしません。

先頭ゼロの扱いパターン例マッチ例非マッチ例
許容\d+0, 0123なし
拒否(0|[1-9]\d*)0, 1230123

用途に応じて使い分けてください。

特に金融やIDなどの用途では先頭ゼロの扱いが重要になることがあります。

これらの要素を組み合わせて、小数点を含む数値の正規表現パターンを柔軟に作成できます。

実用パターン例と比較

最小構成パターン

小数点を含む数値を判定するための最もシンプルな正規表現は、必要最低限の要素だけで構成されたパターンです。

例えば、符号の有無、整数部、小数点、小数部の基本的な組み合わせを表現します。

代表的な最小構成パターンは以下の通りです。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 符号はオプション、整数部は1桁以上、小数点、小数部は0桁以上
        string pattern = @"^[+-]?(\d+\.\d*|\.\d+)$";
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123" };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
+123.456 → True
-.456 → True
123. → True
.456 → True
123 → False

このパターンは、整数部が1桁以上の数字と小数点、小数部が0桁以上の数字、または整数部が省略され小数点から始まる数値にマッチします。

整数のみの数値(例:123)はマッチしません。

最小限の構成で小数点を含む数値を判定したい場合に適しています。

明示的キャプチャを使うパターン

正規表現のグループに名前を付ける「名前付きキャプチャ」を使うと、マッチした部分を後で参照しやすくなります。

特に複雑なパターンや後続処理で数値の各部分を取り出したい場合に便利です。

以下は名前付きキャプチャを使った例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 符号、整数部、小数点、小数部を名前付きキャプチャで分ける
        string pattern = @"^(?<sign>[+-])?(?<integer>\d+)?(\.(?<fraction>\d+))?$";
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123", "-123" };
        foreach (var value in testValues)
        {
            var match = Regex.Match(value, pattern);
            if (match.Success)
            {
                string sign = match.Groups["sign"].Value;
                string integer = match.Groups["integer"].Value;
                string fraction = match.Groups["fraction"].Value;
                Console.WriteLine($"入力: {value}");
                Console.WriteLine($"  符号: {(string.IsNullOrEmpty(sign) ? "(なし)" : sign)}");
                Console.WriteLine($"  整数部: {(string.IsNullOrEmpty(integer) ? "(なし)" : integer)}");
                Console.WriteLine($"  小数部: {(string.IsNullOrEmpty(fraction) ? "(なし)" : fraction)}");
                Console.WriteLine();
            }
            else
            {
                Console.WriteLine($"{value} はマッチしません");
            }
        }
    }
}
入力: 123.456
  符号: (なし)
  整数部: 123
  小数部: 456

入力: +123.456
  符号: +
  整数部: 123
  小数部: 456

入力: -.456
  符号: -
  整数部: (なし)
  小数部: 456

123. はマッチしません
入力: .456
  符号: (なし)
  整数部: (なし)
  小数部: 456

入力: 123
  符号: (なし)
  整数部: 123
  小数部: (なし)

入力: -123
  符号: -
  整数部: 123
  小数部: (なし)

このパターンは整数部と小数部の両方をオプションにしているため、整数のみの数値や小数点のみの数値もマッチします。

名前付きキャプチャを使うことで、各部分を簡単に取り出せるため、後続の処理で数値の解析や変換がしやすくなります。

コメントモードで可読性を高めたパターン

複雑な正規表現は一見して理解しづらいため、C#のRegexOptions.IgnorePatternWhitespaceを使い、コメントや改行を含めて可読性を高める方法があります。

これにより、パターンの各部分に説明を付けられ、メンテナンスがしやすくなります。

以下はコメントモードを使った例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"
            ^                       # 文字列の先頭
            [+-]?                   # 符号(+ または -)はオプション
            (
                (\d+)\.(\d*)        # 整数部1桁以上 + 小数点 + 小数部0桁以上

                |

                \.(\d+)             # 整数部なし + 小数点 + 小数部1桁以上
            )
            $                       # 文字列の末尾
        ";
        var options = RegexOptions.IgnorePatternWhitespace;
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123" };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern, options);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
+123.456 → True
-.456 → True
123. → True
.456 → True
123 → False

このパターンは最小構成パターンと同じ動作をしますが、コメントが付いているため、どの部分が何を意味しているかが一目でわかります。

特にチーム開発や長期間のメンテナンスが必要な場合におすすめです。

これらのパターン例は用途や目的に応じて使い分けることができます。

最小構成はシンプルで高速、名前付きキャプチャは解析に便利、コメントモードは可読性重視といった特徴があります。

実際の開発ではこれらを参考に、要件に合ったパターンを選択してください。

C#コードへの組み込み

Regex.IsMatch の基本使用

C#で正規表現を使って文字列が特定のパターンにマッチするかどうかを判定するには、Regex.IsMatchメソッドが最も簡単でよく使われます。

Regex.IsMatchは、対象の文字列と正規表現パターンを渡すだけで、マッチすればtrue、しなければfalseを返します。

小数点を含む数値の判定例を示します。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 小数点を含む数値の正規表現パターン
        string pattern = @"^[+-]?(\d+\.\d*|\.\d+)$";
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123", "abc", "12.34.56" };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
+123.456 → True
-.456 → True
123. → True
.456 → True
123 → False
abc → False
12.34.56 → False

このコードでは、Regex.IsMatchにパターンと文字列を渡すだけで判定できるため、非常にシンプルに使えます。

小数点を含む数値の判定において、整数のみや不正な文字列はfalseとなります。

事前コンパイルで高速化

Regex.IsMatchを繰り返し呼び出す場合、毎回正規表現パターンを解析して実行するため、パフォーマンスに影響が出ることがあります。

これを改善するために、Regexオブジェクトを事前に作成し、RegexOptions.Compiledオプションを指定してコンパイル済みの正規表現を使う方法があります。

以下は事前コンパイルを使った例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 正規表現をコンパイルしてRegexオブジェクトを作成
        Regex regex = new Regex(@"^[+-]?(\d+\.\d*|\.\d+)$", RegexOptions.Compiled);
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123" };
        foreach (var value in testValues)
        {
            bool isMatch = regex.IsMatch(value);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
+123.456 → True
-.456 → True
123. → True
.456 → True
123 → False

RegexOptions.Compiledを指定すると、正規表現がILコードにコンパイルされるため、繰り返しマッチングを行う場合に高速化が期待できます。

ただし、初回のコンパイルに時間がかかるため、短時間で1回だけ使うケースでは逆に遅くなることもあります。

RegexOptions で挙動を調整

Regexクラスのメソッドには、RegexOptions列挙体を使って挙動を細かく調整できます。

小数点を含む数値の判定でよく使うオプションをいくつか紹介します。

オプション名説明
IgnoreCase大文字・小文字を区別しない(数字には影響なし)
Multiline^$ を行頭・行末にマッチさせる
Singleline. が改行文字にもマッチする
IgnorePatternWhitespaceパターン内の空白やコメントを無視する
Compiled正規表現をコンパイルして高速化

例えば、パターンをコメント付きで書きたい場合はIgnorePatternWhitespaceを使います。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"
            ^               # 文字列の先頭
            [+-]?           # 符号はオプション
            (               # グループ開始
                \d+\.\d*    # 整数部1桁以上 + 小数点 + 小数部0桁以上
                |           # または
                \.\d+       # 小数点 + 小数部1桁以上
            )
            $               # 文字列の末尾
        ";
        Regex regex = new Regex(pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123" };
        foreach (var value in testValues)
        {
            bool isMatch = regex.IsMatch(value);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
+123.456 → True
-.456 → True
123. → True
.456 → True
123 → False

このようにRegexOptionsを活用することで、パターンの可読性を上げたり、パフォーマンスを改善したりできます。

用途に応じて適切なオプションを選択してください。

バリデーションユースケース

ユーザー入力フォーム

ユーザーがWebフォームやデスクトップアプリの入力欄に数値を入力する際、小数点を含む数値かどうかを正確に判定することは重要です。

正規表現を使うことで、入力値が期待するフォーマットかどうかを即座にチェックできます。

例えば、商品の価格や数量、測定値などの入力欄で、小数点を含む数値のみを許可したい場合、以下のようにRegex.IsMatchを使ってバリデーションを行います。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?(\d+\.\d*|\.\d+)$";
        Console.WriteLine("数値を入力してください(小数点を含む数値のみ有効):");
        string input = Console.ReadLine();
        if (Regex.IsMatch(input, pattern))
        {
            Console.WriteLine("有効な小数点数値です。");
        }
        else
        {
            Console.WriteLine("無効な入力です。小数点を含む数値を入力してください。");
        }
    }
}
数値を入力してください(小数点を含む数値のみ有効):123.45
有効な小数点数値です。

このように、ユーザーが誤った形式の数値を入力した場合に即座にエラーメッセージを表示できるため、入力ミスを減らし、データの品質を保てます。

さらに、リアルタイムでの入力チェックや、フォーム送信前の最終確認にも活用できます。

CSV項目検証

CSVファイルなどのテキストデータから数値を読み込む際、各項目が正しい小数点数値かどうかを検証することが求められます。

特に大量のデータを扱う場合、正規表現で効率的にバリデーションを行うことが便利です。

以下はCSVの各フィールドを読み込み、小数点を含む数値かどうかを判定する例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?(\d+\.\d*|\.\d+)$";
        string csvLine = "100.5,+200.0,-.75,300,abc";
        string[] fields = csvLine.Split(',');
        for (int i = 0; i < fields.Length; i++)
        {
            string field = fields[i];
            bool isValid = Regex.IsMatch(field, pattern);
            Console.WriteLine($"フィールド[{i}]: {field}{(isValid ? "有効" : "無効")}");
        }
    }
}
フィールド[0]: 100.5 → 有効
フィールド[1]: +200.0 → 有効
フィールド[2]: -.75 → 有効
フィールド[3]: 300 → 無効
フィールド[4]: abc → 無効

この例では、小数点を含む数値のみを有効とし、整数のみや文字列は無効と判定しています。

CSVのデータ品質チェックや、数値項目の前処理に役立ちます。

API受信データのサニタイズ

外部APIから受信したデータに数値が含まれる場合、想定外の形式や不正な値が混入していることがあります。

特に小数点を含む数値を期待している場合は、正規表現で受信データを検証し、サニタイズ(無効なデータの除去や修正)を行うことが重要です。

以下はAPIから受け取った文字列配列を検証し、小数点を含む数値のみを抽出する例です。

using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?(\d+\.\d*|\.\d+)$";
        string[] apiData = { "12.34", "-56.78", "90", "abc", "+.123", "45.6.7" };
        List<string> sanitizedData = new List<string>();
        foreach (var item in apiData)
        {
            if (Regex.IsMatch(item, pattern))
            {
                sanitizedData.Add(item);
            }
        }
        Console.WriteLine("サニタイズ後のデータ:");
        foreach (var value in sanitizedData)
        {
            Console.WriteLine(value);
        }
    }
}
サニタイズ後のデータ:
12.34
-56.78
+.123

この方法で、APIからの不正なデータを除外し、後続の処理で安全に数値を扱えます。

特に金融や計測データなど、正確な数値が求められるシステムで有効です。

よくある失敗パターン

二重ドット .. を許してしまう

小数点を含む数値の正規表現を作成する際に、誤って「二重ドット..」を許してしまうケースがあります。

これは、小数点の位置や回数を正しく制御できていないために起こります。

例えば、以下のようなパターンは注意が必要です。

パターン例:

^[+-]?[\d.]+$ このパターンは、数字とドットが1回以上連続する文字列にマッチしますが、ドットの数や位置を制限していないため、12..34...のような不正な文字列にもマッチしてしまいます。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?[\d.]+$";
        string[] testValues = { "12.34", "12..34", "1.2.3", "123", "..." };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
12.34 → True
12..34 → True
1.2.3 → True
123 → True
... → True

このように、二重ドットや複数のドットを含む文字列もマッチしてしまい、正しい小数点数値の判定としては不十分です。

対策としては、小数点は1回だけ、かつ整数部と小数部の位置を明確に分けるパターンを使うことです。

例えば、^[+-]?(\d+\.\d*|\.\d+)$のように、ドットの位置を限定し、複数回のドットを許さない構造にします。

数字だけパターンとの誤用

小数点を含む数値の判定で、整数だけを判定するパターンを誤って使ってしまうことがあります。

例えば、\d+だけのパターンは整数にマッチしますが、小数点を含む数値はマッチしません。

逆に、小数点を含む数値のパターンを使って整数を判定しようとしても、整数のみの文字列はマッチしないことがあります。

  • パターン:^\d+$
    • マッチ:123
    • マッチしない:123.45
  • パターン:^[+-]?(\d+\.\d*|\.\d+)$
    • マッチ:123.45, .45
    • マッチしない:123

このため、整数も小数も許容したい場合は、パターンを工夫する必要があります。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?(\d+(\.\d*)?|\.\d+)$"; // 整数部と小数部の両方を許容
        string[] testValues = { "123", "123.45", ".45", "abc", "123." };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123 → True
123.45 → True
.45 → True
abc → False
123. → True

整数と小数の両方を許容したい場合は、このように整数部の後に小数部が0回または1回続く形を含めるパターンにするのがポイントです。

全角数字への未対応

日本語環境などで全角数字が入力されることがありますが、\dは半角数字(0-9)のみを対象としています。

そのため、全角数字を含む文字列は正規表現でマッチしません。

  • 半角数字:123 → マッチ
  • 全角数字:123 → マッチしない
using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^\d+$";
        string halfWidth = "123";
        string fullWidth = "123";
        Console.WriteLine($"半角数字: {Regex.IsMatch(halfWidth, pattern)}");
        Console.WriteLine($"全角数字: {Regex.IsMatch(fullWidth, pattern)}");
    }
}
半角数字: True
全角数字: False

全角数字を許容したい場合は、正規表現に全角数字のUnicode範囲を含める必要があります。

全角数字はUnicodeで[0-9]に該当します。

全角数字を含める例:

string pattern = @"^[\d0-9]+$";

ただし、小数点や符号も全角で入力される可能性がある場合は、それらも含めてパターンを拡張する必要があります。

全角文字を許容するかどうかは、アプリケーションの要件に応じて判断してください。

多くの場合、入力前に半角に変換する処理を入れることが推奨されます。

拡張ニーズへの対応

指数表記 e E 付き

科学技術計算や大きな数値、小さな数値を扱う場合、指数表記(エクスポネンシャル表記)がよく使われます。

C#で小数点を含む数値に加えて、指数表記を正規表現で判定したい場合は、eまたはEに続く指数部分をパターンに組み込む必要があります。

指数表記の基本構造は以下の通りです。

  • 数値本体(整数部と小数部)
  • e または E
  • 符号+ または -がオプションの指数部(整数)

これを踏まえた正規表現の例を示します。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 小数点数値+指数表記のパターン
        string pattern = @"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$";
        string[] testValues = {
            "123.456",
            "-123.456e10",
            "+.456E-5",
            "0.123e+3",
            "123e5",
            "123.",
            ".456",
            "123",
            "abc",
            "1.2.3",
            "123e"
        };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
123.456 → True
-123.456e10 → True
+.456E-5 → True
0.123e+3 → True
123e5 → True
123. → True
.456 → True
123 → True
abc → False
1.2.3 → False
123e → False

このパターンのポイントは、指数部([eE][+-]?\d+)?をオプションとして追加していることです。

指数部はeまたはEに続き、符号があってもなくてもよく、1桁以上の数字が続きます。

これにより、123.456e10+.456E-5のような指数表記も正しくマッチします。

パーセンテージ記号付き

数値の後ろにパーセンテージ記号%が付くケースもよくあります。

例えば、割合や成長率などの入力で使われます。

パーセンテージ記号を含めて判定したい場合は、正規表現の末尾に%をオプションで追加します。

以下はパーセンテージ記号を許容するパターンの例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 小数点数値+指数表記+パーセント記号のパターン
        string pattern = @"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?%?$";
        string[] testValues = {
            "50%",
            "100.0%",
            "-12.5%",
            "+.75%",
            "0.123e2%",
            "123",
            "123.45",
            "abc%",
            "123.%"
        };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
50% → True
100.0% → True
-12.5% → True
+.75% → True
0.123e2% → True
123 → True
123.45 → True
abc% → False
123.% → True

パーセンテージ記号は%?で0回または1回の出現を許可しています。

これにより、50%-12.5%のような表記もマッチし、パーセント記号なしの数値もそのままマッチします。

通貨フォーマットとの組み合わせ

通貨表記では、数値の前に通貨記号(例:$¥)が付くことがあります。

また、桁区切りのカンマやスペースが含まれる場合もあります。

これらを含めて小数点数値を判定したい場合は、正規表現を拡張する必要があります。

以下は、通貨記号のオプションとカンマ区切りを簡単に許容する例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 通貨記号($、¥、€)オプション、カンマ区切り、小数点数値のパターン
        string pattern = @"^[+-]?[\$\¥\€]?(\d{1,3}(,\d{3})*|\d+)(\.\d+)?$";
        string[] testValues = {
            "$1,234.56",
            "¥123456.78",
            "€12,345",
            "+$1,000",
            "-1234.56",
            "1234",
            "12,34.56",  // 不正なカンマ位置
            "$1234.56.78" // 不正な小数点
        };
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
$1,234.56 → True
¥123456.78 → True
€12,345 → True
+$1,000 → True
-1234.56 → True
1234 → True
12,34.56 → False
$1234.56.78 → False

このパターンのポイントは以下の通りです。

  • 通貨記号は[\$\¥\€]?で0回または1回の出現を許可しています。必要に応じて他の通貨記号も追加可能です
  • 整数部はカンマ区切りを考慮し、\d{1,3}(,\d{3})*で3桁ごとのカンマを許容しています
  • 小数部はオプションで、\.\d+で1桁以上の数字を指定しています

ただし、このパターンはカンマの位置が正しい場合のみマッチし、不正なカンマ位置はマッチしません。

より厳密な通貨フォーマットの検証が必要な場合は、専用のパーサーやライブラリの利用を検討してください。

これらの拡張パターンを活用することで、より多様な数値表現に対応したバリデーションが可能になります。

用途に応じて正規表現をカスタマイズし、正確かつ柔軟な数値判定を実現してください。

多言語・カルチャー依存の注意点

小数点記号がカンマの文化圏

世界には小数点の記号として「カンマ(,)」を使う文化圏があります。

例えば、ドイツ語圏やフランス語圏など多くのヨーロッパ諸国では、小数点の代わりにカンマを用います。

一方で、英語圏や日本では「ピリオド(.)」が小数点記号です。

この違いは数値の表記や入力に大きく影響します。

C#の正規表現で小数点を判定する際に、単純に\.(ピリオド)だけを許容していると、カンマを小数点として使う文化圏の数値はマッチしません。

  • 英語圏の小数点表記:123.45
  • ドイツ語圏の小数点表記:123,45

もしアプリケーションが多言語対応で、ユーザーの文化圏に応じた数値入力を受け付ける場合は、小数点記号をカンマに切り替えたり、両方を許容したりする必要があります。

正規表現で両方を許容する例:

string pattern = @"^[+-]?(\d+([.,]\d*)?|[.,]\d+)$";

このパターンでは、小数点記号としてピリオド.またはカンマ,のどちらかを許容しています。

ただし、ピリオドとカンマの両方が混在する数値(例:1,234.56)は別途処理が必要です。

NumberFormatInfo と連携させる方法

C#では、System.Globalization.NumberFormatInfoクラスを使って、カルチャーごとの数値フォーマット情報を取得できます。

これを活用すると、正規表現のパターンを動的に生成し、ユーザーのカルチャーに合わせた小数点記号を判定できます。

以下は、現在のカルチャーの小数点記号を取得し、それを正規表現に組み込む例です。

using System;
using System.Globalization;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 現在のカルチャーのNumberFormatInfoを取得
        NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
        // 小数点記号をエスケープ(正規表現で特別な意味を持つ場合に備える)
        string decimalSeparator = Regex.Escape(nfi.NumberDecimalSeparator);
        // 正規表現パターンを動的に作成
        string pattern = $@"^[+-]?(\d+({decimalSeparator}\d*)?|{decimalSeparator}\d+)$";
        string[] testValues = { "123.45", "123,45", "+123.45", "-.45", ".45", "123" };
        Console.WriteLine($"現在のカルチャー: {CultureInfo.CurrentCulture.Name}");
        Console.WriteLine($"小数点記号: '{nfi.NumberDecimalSeparator}'");
        Console.WriteLine();
        foreach (var value in testValues)
        {
            bool isMatch = Regex.IsMatch(value, pattern);
            Console.WriteLine($"{value}{isMatch}");
        }
    }
}
現在のカルチャー: ja-JP
小数点記号: '.'

123.45 → True
123,45 → False
+123.45 → True
-.45 → True
.45 → True
123 → True

この例では、CultureInfo.CurrentCultureの小数点記号に合わせて正規表現を生成しています。

日本やアメリカのカルチャーではピリオドが小数点なので、123.45はマッチしますが、123,45はマッチしません。

もしカルチャーをドイツde-DEなどに切り替えると、小数点記号がカンマに変わり、123,45がマッチするようになります。

CultureInfo.CurrentCulture = new CultureInfo("de-DE");

このように、NumberFormatInfoと連携させることで、ユーザーの文化圏に適した数値判定が可能になります。

多言語対応のアプリケーションでは、この方法を使って正規表現を動的に調整することをおすすめします。

テストとデバッグの手法

Unit Test で期待値を固定

正規表現を使った小数点を含む数値の判定は、さまざまな入力パターンに対して正しく動作することを保証する必要があります。

Unit Test(単体テスト)を活用して、期待される入力と出力を固定し、正規表現の動作を自動的に検証することが効果的です。

C#の代表的なテストフレームワークであるxUnitを使った例を示します。

using System.Text.RegularExpressions;
using Xunit;
public class DecimalNumberRegexTests
{
    private const string Pattern = @"^[+-]?(\d+(\.\d*)?|\.\d+)$";
    [Theory]
    [InlineData("123.456", true)]
    [InlineData("+123.456", true)]
    [InlineData("-.456", true)]
    [InlineData("123.", true)]
    [InlineData(".456", true)]
    [InlineData("123", true)]
    [InlineData("abc", false)]
    [InlineData("12.34.56", false)]
    [InlineData("", false)]
    public void TestDecimalNumberRegex(string input, bool expected)
    {
        bool actual = Regex.IsMatch(input, Pattern);
        Assert.Equal(expected, actual);
    }
}

このテストコードでは、さまざまな文字列を用意し、正規表現が期待通りにマッチするかどうかを検証しています。

[Theory][InlineData]を使うことで複数のテストケースを簡潔に記述でき、テストの網羅性を高められます。

Unit Testを導入することで、正規表現の修正や拡張を行った際に既存の動作が壊れていないかを自動でチェックでき、品質の維持に役立ちます。

プロファイラでパフォーマンス計測

正規表現は強力ですが、複雑なパターンや大量のデータに対して繰り返しマッチングを行うとパフォーマンスに影響が出ることがあります。

特にWebアプリケーションやリアルタイム処理では、正規表現の実行速度が重要です。

Visual Studioには組み込みのプロファイラがあり、CPU使用率やメソッドの実行時間を計測できます。

これを使って、正規表現のマッチング処理がどの程度の負荷をかけているかを把握できます。

簡単な手順は以下の通りです。

  1. Visual Studioでプロジェクトを開きます。
  2. メニューから「デバッグ」→「パフォーマンスプロファイラー」を選択。
  3. 「CPU使用率」などの計測項目を選択し、アプリケーションを実行。
  4. 正規表現を使った処理を含むシナリオを操作。
  5. プロファイラの結果で、正規表現関連のメソッドの実行時間を確認。

これにより、正規表現のパフォーマンスボトルネックを特定し、必要に応じてパターンの見直しやRegexOptions.Compiledの利用、キャッシュの導入などの対策を検討できます。

Regexツールで視覚的検証

正規表現は複雑になりやすく、パターンの意図通りに動作しているかを目視で確認するのは難しいことがあります。

そこで、視覚的に正規表現を検証できるツールを活用すると効率的です。

代表的なツールには以下があります。

  • Regex101(https://regex101.com/)

Webベースで、リアルタイムに正規表現のマッチ結果を確認でき、各部分の意味やキャプチャグループも表示されます。

C#の正規表現モードも選択可能です。

  • RegexBuddy

Windowsアプリケーションで、正規表現の作成、テスト、デバッグに特化したツールです。

詳細な説明やテストケース管理が可能です。

  • Visual Studioの正規表現エディタ

Visual Studioの一部機能として、正規表現のテストができます。

拡張機能を入れるとさらに便利になります。

これらのツールを使うと、以下のようなメリットがあります。

  • 入力文字列に対してどの部分がマッチしているかを色分けで視覚化できます
  • キャプチャグループの内容を即座に確認できます
  • 正規表現の構文エラーや警告を検出できます
  • 複雑なパターンの動作を段階的に検証できます

例えば、^[+-]?(\d+(\.\d*)?|\.\d+)$というパターンをRegex101に貼り付け、テスト文字列を入力すると、どの部分が整数部、小数部、符号にマッチしているかが一目でわかります。

このように視覚的検証ツールを活用することで、正規表現の理解が深まり、バグの早期発見や修正が容易になります。

開発効率の向上にぜひ取り入れてください。

セキュリティ観点

ReDoS への対策

正規表現は強力な文字列マッチングツールですが、不適切なパターン設計により「ReDoS(Regular Expression Denial of Service)」と呼ばれる脆弱性を引き起こすことがあります。

ReDoSは、悪意のある入力に対して正規表現の処理が極端に遅くなり、サービスの応答が停止または遅延する攻撃です。

ReDoSの原因は、正規表現の「バックトラッキング」が過剰に発生するパターンにあります。

特に、量指定子*+?や選択肢|が複雑に絡み合い、同じ部分文字列を何度も試行する場合に起こりやすいです。

小数点を含む数値の正規表現でも、以下のようなパターンは注意が必要です。

^[+-]?(\d+(\.\d*)?|\.\d+)$

このパターン自体は比較的安全ですが、もし量指定子を多用しすぎたり、曖昧な繰り返しを組み合わせるとReDoSのリスクが高まります。

ReDoS対策のポイント

  • 量指定子の使い方を慎重にする

例えば、.*.+のような貪欲な繰り返しは避け、必要な範囲を限定します。

  • アンカーを使う

^(先頭)と$(末尾)を使い、文字列全体を対象にすることで無駄なバックトラッキングを減らす。

  • 正規表現の複雑さを抑える

複雑な選択肢やネストを減らし、シンプルなパターンにします。

  • 入力の事前検証を行う

正規表現を適用する前に、入力の長さや文字種を制限します。

  • RegexOptions.Compiledを使う

コンパイル済み正規表現はパフォーマンスが向上し、バックトラッキングの影響を軽減できる場合があります。

  • 正規表現のテストツールで脆弱性をチェックする

ReDoS脆弱性を検出するツールやオンラインサービスを活用します。

入力長制限とタイムアウト

ReDoS攻撃を防ぐためには、入力文字列の長さを制限することが基本的な対策です。

非常に長い文字列に対して正規表現を適用すると、処理時間が指数関数的に増加する可能性があるため、入力の最大長を設定して不正な長さの入力を拒否します。

const int MaxInputLength = 1000;
string input = GetUserInput();
if (input.Length > MaxInputLength)
{
    Console.WriteLine("入力が長すぎます。");
    return;
}

また、.NETのRegexクラスでは、Regex.MatchRegex.IsMatchにタイムアウトを設定することができます。

これにより、正規表現の処理が一定時間を超えた場合に例外をスローし、無限ループや過剰なバックトラッキングによるサービス停止を防げます。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string pattern = @"^[+-]?(\d+(\.\d*)?|\.\d+)$";
        string input = GetUserInput();
        try
        {
            // タイムアウトを1秒に設定
            bool isMatch = Regex.IsMatch(input, pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
            Console.WriteLine($"マッチ結果: {isMatch}");
        }
        catch (RegexMatchTimeoutException)
        {
            Console.WriteLine("正規表現の処理がタイムアウトしました。入力を確認してください。");
        }
    }
    static string GetUserInput()
    {
        // ここでは例として長い文字列を返す
        return new string('1', 10000) + ".";
    }
}

このようにタイムアウトを設定することで、処理が長時間かかる入力に対して安全に対応できます。

これらのセキュリティ対策を組み合わせることで、正規表現を使った小数点数値の判定においても、サービスの安定性と安全性を確保できます。

特に外部からの入力を扱う場合は、ReDoSのリスクを意識し、適切な制限と監視を行うことが重要です。

メンテナンス性を高める書き方

名前付きキャプチャの活用

正規表現は複雑になりやすく、特に複数のグループを使う場合はどの部分が何を表しているのか分かりづらくなります。

名前付きキャプチャを使うことで、グループに意味のある名前を付けられ、コードの可読性と保守性が大幅に向上します。

名前付きキャプチャは、(?<名前>パターン)の形式で記述します。

これにより、マッチした結果から名前でグループを参照できるため、インデックス番号に頼る必要がなくなります。

以下は、小数点を含む数値の正規表現に名前付きキャプチャを使った例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        // 名前付きキャプチャを使った正規表現パターン
        string pattern = @"^(?<sign>[+-])?(?<integer>\d+)?(\.(?<fraction>\d+))?$";
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123", "-123" };
        foreach (var value in testValues)
        {
            var match = Regex.Match(value, pattern);
            if (match.Success)
            {
                string sign = match.Groups["sign"].Value;
                string integer = match.Groups["integer"].Value;
                string fraction = match.Groups["fraction"].Value;
                Console.WriteLine($"入力: {value}");
                Console.WriteLine($"  符号: {(string.IsNullOrEmpty(sign) ? "(なし)" : sign)}");
                Console.WriteLine($"  整数部: {(string.IsNullOrEmpty(integer) ? "(なし)" : integer)}");
                Console.WriteLine($"  小数部: {(string.IsNullOrEmpty(fraction) ? "(なし)" : fraction)}");
                Console.WriteLine();
            }
            else
            {
                Console.WriteLine($"{value} はマッチしません");
            }
        }
    }
}
入力: 123.456
  符号: (なし)
  整数部: 123
  小数部: 456
入力: +123.456
  符号: +
  整数部: 123
  小数部: 456
入力: -.456
  符号: -
  整数部: (なし)
  小数部: 456
入力: 123.
  符号: (なし)
  整数部: 123
  小数部: (なし)
入力: .456
  符号: (なし)
  整数部: (なし)
  小数部: 456
入力: 123
  符号: (なし)
  整数部: 123
  小数部: (なし)
入力: -123
  符号: -
  整数部: 123
  小数部: (なし)

このように名前付きキャプチャを使うと、後からコードを読む人がどの部分が符号で、どの部分が整数部や小数部かをすぐに理解できます。

特に複数のグループを扱う場合は、インデックス番号の誤りを防ぐ効果もあります。

定数定義とコメントの付加

正規表現は一見して意味が分かりにくいため、定数としてパターンを定義し、適切なコメントを付けることでメンテナンス性を高められます。

定数にすることで、パターンの変更箇所が明確になり、再利用もしやすくなります。

また、正規表現の各部分に対してコメントを付けることで、何を意図しているのかを明示できます。

C#では@""の文字列リテラルを使い、複数行にわたる文字列を記述しやすくなっています。

以下は定数定義とコメントを付けた例です。

using System;
using System.Text.RegularExpressions;
class Program
{
    // 小数点を含む数値の正規表現パターン
    // 符号(+/-)はオプション
    // 整数部は0回または1回の出現(省略可能)
    // 小数点以下はオプションで、存在する場合は1桁以上の数字
    private const string DecimalNumberPattern = @"
        ^                       # 文字列の先頭
        (?<sign>[+-])?           # 符号(+ または -)はオプション
        (?<integer>\d+)?         # 整数部は0回または1回の数字の繰り返し
        (\.(?<fraction>\d+))?    # 小数点以下はオプションで数字1回以上
        $                       # 文字列の末尾
    ";
    static void Main()
    {
        var regex = new Regex(DecimalNumberPattern, RegexOptions.IgnorePatternWhitespace);
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123", "-123" };
        foreach (var value in testValues)
        {
            var match = regex.Match(value);
            if (match.Success)
            {
                Console.WriteLine($"入力: {value}");
                Console.WriteLine($"  符号: {(string.IsNullOrEmpty(match.Groups["sign"].Value) ? "(なし)" : match.Groups["sign"].Value)}");
                Console.WriteLine($"  整数部: {(string.IsNullOrEmpty(match.Groups["integer"].Value) ? "(なし)" : match.Groups["integer"].Value)}");
                Console.WriteLine($"  小数部: {(string.IsNullOrEmpty(match.Groups["fraction"].Value) ? "(なし)" : match.Groups["fraction"].Value)}");
                Console.WriteLine();
            }
            else
            {
                Console.WriteLine($"{value} はマッチしません");
            }
        }
    }
}
入力: 123.456
  符号: (なし)
  整数部: 123
  小数部: 456
入力: +123.456
  符号: +
  整数部: 123
  小数部: 456
入力: -.456
  符号: -
  整数部: (なし)
  小数部: 456
入力: 123.
  符号: (なし)
  整数部: 123
  小数部: (なし)
入力: .456
  符号: (なし)
  整数部: (なし)
  小数部: 456
入力: 123
  符号: (なし)
  整数部: 123
  小数部: (なし)
入力: -123
  符号: -
  整数部: 123
  小数部: (なし)

このように、正規表現パターンを定数として分離し、RegexOptions.IgnorePatternWhitespaceを使ってコメントを含めることで、パターンの意味が明確になり、将来的な修正や拡張がしやすくなります。

名前付きキャプチャと定数定義、コメント付加を組み合わせることで、正規表現のメンテナンス性を大幅に向上させられます。

特にチーム開発や長期運用のプロジェクトでは、これらの工夫が品質維持に役立ちます。

代替手段の検討

double.TryParse との機能比較

C#で小数点を含む数値の判定を行う際、正規表現を使う方法のほかに、double.TryParseメソッドを利用する方法があります。

double.TryParseは文字列を浮動小数点数に変換できるかどうかを判定し、変換に成功すればtrueを返します。

これにより、数値として有効かどうかを簡単にチェックできます。

double.TryParseの特徴

  • カルチャー依存の数値解析が可能

TryParseNumberStylesIFormatProviderを指定でき、カルチャーに応じた小数点記号や桁区切りを自動的に処理します。

  • 指数表記や符号付き数値も対応

1.23e4-123.45などの表記も正しく解析できます。

  • パフォーマンスが高い

内部で最適化されたパーサーを使うため、正規表現より高速な場合が多いです。

  • 数値としての妥当性を保証

文字列が実際にdouble型として解釈可能かどうかを判定するため、数値としての意味を持つかどうかが確実です。

正規表現との比較

項目正規表現double.TryParse
柔軟性パターン次第で細かく制御可能カルチャー依存の解析が可能
可読性複雑になりやすいシンプルなAPI呼び出し
パフォーマンス複雑なパターンは遅くなることも高速で最適化されている
エラー検出パターンに合致しない場合のみ判定変換失敗で詳細なエラーは得にくい
拡張性複雑なフォーマットも対応可能標準的な数値フォーマットに限定

使用例

using System;
using System.Globalization;
class Program
{
    static void Main()
    {
        string[] testValues = { "123.456", "+123.456", "-.456", "123.", ".456", "123", "abc", "1.23e4" };
        foreach (var value in testValues)
        {
            bool isValid = double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out double result);
            Console.WriteLine($"{value}{isValid} (Parsed value: {(isValid ? result.ToString() : "N/A")})");
        }
    }
}
123.456 → True (Parsed value: 123.456)
+123.456 → True (Parsed value: 123.456)
-.456 → True (Parsed value: -0.456)

123. → True (Parsed value: 123)

.456 → True (Parsed value: 0.456)
123 → True (Parsed value: 123)
abc → False (Parsed value: N/A)
1.23e4 → True (Parsed value: 12300)

このように、double.TryParseは多くの数値表現に対応し、正規表現よりも簡潔かつ堅牢に数値判定が可能です。

カスタムパーサー実装の選択肢

正規表現やdouble.TryParseで対応しきれない特殊なフォーマットや独自の数値表現を扱う場合、カスタムパーサーを実装する選択肢があります。

カスタムパーサーは文字列を一文字ずつ解析し、独自のルールに基づいて数値の妥当性を判定・変換します。

カスタムパーサーを選ぶ理由

  • 特殊なフォーマット対応

例えば、通貨記号や単位が混在する文字列、複雑な区切り文字、独自の指数表記など。

  • パフォーマンス最適化

特定の用途に特化した処理を行うことで、不要な処理を省き高速化できます。

  • 詳細なエラー情報の提供

どの位置でどのようなエラーが起きたかを細かく報告できます。

  • 拡張性と保守性

独自仕様に合わせて柔軟に拡張可能です。

実装のポイント

  • 文字列をループで走査し、各文字の意味を判定します
  • 符号、小数点、数字、指数部などの状態を管理するステートマシンを設計します
  • 不正な文字や不適切な位置の文字を検出し、即座にエラーとします
  • 解析結果を数値型に変換するか、必要に応じて構造体やクラスで返します

簡単な例

using System;
class CustomDecimalParser
{
    public static bool TryParse(string input, out double result)
    {
        result = 0;
        if (string.IsNullOrEmpty(input))
            return false;
        int pos = 0;
        int length = input.Length;
        bool isNegative = false;
        // 符号チェック
        if (input[pos] == '+')
        {
            pos++;
        }
        else if (input[pos] == '-')
        {
            isNegative = true;
            pos++;
        }
        double integerPart = 0;
        double fractionPart = 0;
        double fractionDivisor = 1;
        bool fractionMode = false;
        while (pos < length)
        {
            char c = input[pos];
            if (c == '.')
            {
                if (fractionMode) // 2回目の小数点はエラー
                    return false;
                fractionMode = true;
            }
            else if (char.IsDigit(c))
            {
                int digit = c - '0';
                if (!fractionMode)
                {
                    integerPart = integerPart * 10 + digit;
                }
                else
                {
                    fractionDivisor *= 10;
                    fractionPart += digit / fractionDivisor;
                }
            }
            else
            {
                // 不正な文字
                return false;
            }
            pos++;
        }
        result = integerPart + fractionPart;
        if (isNegative)
            result = -result;
        return true;
    }
}
class Program
{
    static void Main()
    {
        string[] testValues = { "123.456", "-123.456", "+.456", "123.", ".456", "abc", "12.34.56" };
        foreach (var value in testValues)
        {
            bool success = CustomDecimalParser.TryParse(value, out double parsed);
            Console.WriteLine($"{value}{success} (Parsed: {(success ? parsed.ToString() : "N/A")})");
        }
    }
}
123.456 → True (Parsed: 123.456)
-123.456 → True (Parsed: -123.456)
+.456 → True (Parsed: 0.456)

123. → True (Parsed: 123)

.456 → True (Parsed: 0.456)
abc → False (Parsed: N/A)
12.34.56 → False (Parsed: N/A)

この例は非常にシンプルな実装ですが、独自のルールを追加したり、指数表記やパーセント記号の処理を加えたりすることも可能です。

正規表現やdouble.TryParseで対応できない特殊な要件がある場合は、カスタムパーサーの実装を検討してください。

ただし、実装コストやテストの手間が増えるため、まずは標準機能で対応可能かを十分に検討することが重要です。

まとめ

C#で小数点を含む数値を正規表現で判定する際は、基本要素の理解と適切なパターン設計が重要です。

名前付きキャプチャやコメント付きパターンで可読性を高め、RegexOptionsや事前コンパイルで性能を最適化できます。

多言語対応や指数表記、通貨フォーマットなどの拡張も可能です。

セキュリティ面ではReDoS対策や入力制限、タイムアウト設定が必須です。

場合によってはdouble.TryParseやカスタムパーサーの利用も検討しましょう。

関連記事

Back to top button
目次へ