【C#】正規表現で小数文字列をマッチする

小数文字列は書き方が複数ありますが、それらを正規表現でマッチさせたりマッチした文字列を取り出す方法を紹介したいと思います。

小数の表記方法の確認

小数は以下のように表現されます。

  • 123.456 / +123.456
  • -123.456
  • .456 / +.456
  • -.456
  • 1.23e5 / 1.23E5 / 1.23e+5 / 1.23e-5 / 1.23E05

ルールとして

  • 通常のルール
    • 整数部分が無く小数点から始まるときがある
    • 先頭にプラスもしくはマイナスが付くときがある
    • 上記2つのルールは共存する
    • 整数部が指定されたら小数部は省略可能、小数部があれば整数は省略可能
    • 省略表記は数値 + ドット もしくは ドット + 数値となり両方省略(ドットのみ)は不可
  • 指数表記のルール
    • e は大文字/小文字両方使用できる
    • e の後ろには符号(+-)が付くときと付かない時がある
    • eが指定されたら後ろには必ず有効な数値が指定される

が挙げられます。

正規表現の実装

C#実装としての正規表現としては以下の通りです。

// 小数をマッチするための正規表現
string pattern = @"^[+-]?(\d+.\d*|\d*.\d+)([eE][+-]?\d+)?$";

// --- 部分ごとの説明 ---

// ★(1)先頭の符号の指定
[+-]?

// ★(2)数値の指定
// 123. とか .456 のような形式を想定
(\d+.\d*|\d*.\d+)

// ★(3)指数の指定
([eE][+-]?\d+)?

C# で実装すると以下のようになります。

const string Pattern = @"^[+-]?(\d+.\d*|\d*.\d+)([eE][+-]?\d+)?$";
static string[] values = new string[]
{
    // 通常の数値
    "123.456",
    "+123.456",
    "-123.456",
    "123.",
    "+123.",
    ".456",
    "-.456",
    // 指数表記
    "123.456e12",
    "123.456E12",
    "123.456e-12",
    "123.456e012",
    "123.456e-012",
    "+123.456e12",
    "-123.456e12",
    "123.e12",
    "+123.e12",
    ".456e12",
    "-.456e12",
    // 一致しない
    ".",
    "+a.12",
    "123.456f0-1"
};

private static void Check()
{
    foreach (var value in values)
    {
        bool isMatch = Regex.IsMatch(value, Pattern);
        Console.WriteLine($"{isMatch},{value}");
        // True,123.456
        // True,+123.456
        // True,-123.456
        // True,123.
        // True,+123.
        // True,.456
        // True,-.456
        // True,123.456e12
        // True,123.456E12
        // True,123.456e-12
        // True,123.456e012
        // True,123.456e-012
        // True,+123.456e12
        // True,-123.456e12
        // True,123.e12
        // True,+123.e12
        // True,.456e12
        // True,-.456e12
        // False,.
        // False,+a.12
        // False,123.456f0-1
    }
}

部分的に切り出して先頭から(^)末尾まで($)で一致させていますがこれを省略すると "+a.12" や "123.456f0-1" も一致判定となりますが、実際にマッチしている部分は ".12" と "123.456"となります。

この、一致している小数部分を取り出す方法は以下の通りです。

// 小数の部分をグループ化して取り出す文字列
const string Pattern = @"(?<GRP>[+-]?(\d+.\d*|\d*.\d+)([eE][+-]?\d+)?)";

// 検査対象の文字列リスト
static string[] values = new string[]
{
    //...省略...
};

// チェック実行
private static void Check()
{
    foreach (var value in values)
    {
        Match m = Regex.Match(value, Pattern);
        if (m.Success)
        {
            string matchString = m.Groups["GRP"].Value;
            Console.WriteLine($"\"{value}\" -> {matchString}");
        }
        else
        {
            Console.WriteLine($"{value} is not match");
        }
        > "123.456"  -> 123.456
        > "+123.456" -> +123.456
        > "-123.456" -> -123.456
        > "123."  -> 123.
        > "+123." -> +123.
        > ".456"  -> .456
        > "-.456" -> -.456
        > "123.456e12"   -> 123.456e12
        > "123.456E12"   -> 123.456E12
        > "123.456e-12"  -> 123.456e-12
        > "123.456e012"  -> 123.456e012
        > "123.456e-012" -> 123.456e-012
        > "+123.456e12"  -> +123.456e12
        > "-123.456e12"  -> -123.456e12
        > "123.e12"  -> 123.e12
        > "+123.e12" -> +123.e12
        > ".456e12"  -> .456e12
        > "-.456e12" -> -.456e12
        > . is not match
        > "+a.12"       -> .12
        > "123.456f0-1" -> 123.456
    }
}

この取り出した文字列を double.Parse(matchString) とすると浮動小数として取得できます。