数値っぽい表現を判定する正規表現を書いてみた

ググッてもすぐにヒットしなかったので、探すよりも自分で書いた方が早いと思って取り掛かったんですが、意外に時間がかかったので載せておきます。
最終的な正規表現を見るとかなりシンプルなんですが、何も考えないで書くと空文字列にマッチしたり、ドットにマッチしたりするかもしれないんで、意外にトラップが多いです。

is.numexp <- function(strs) {
    grepl("(?x)^\\s*[-+]?
           (?:
               # begin with number
               (?:
                  \\d+  # without commas
                  | (?: 0[1-9]{1,2} | 00[1-9] | [1-9]\\d{0,2} )(?: ,\\d{3} )+  # with commas
               )
               (?: \\.\\d* )?
               # begin with dot
               | \\.\\d+
           )
           (?i: e[-+]\\d* )?  # scientific notation
           \\s*$", strs, perl = TRUE)
}

ちなみに次のようなテストをしています。
01,234は数値と判定するのに0,000を数値と判定しないのは何となくそうしただけなので、気に食わない方は変更すると良いと思います。

library(RUnit)
test.is.numexp <- function() {
    # 数値の文字列(16進数は対象外とする)
    test.cases <- c("1234", "1234.", "1234.00000", "1234.1234", "01234",
                    ".1234", "-1234", "+1234", " 1234", "\t1234",  # 数字以外で始まる
                    "1234E+02", "1.234E+", "1.234E+02", "1.234e-2", ".12e+02",  # 指数表記
                    "1,234", "01,234", "12,345,678", "123,456,789", "10,234",  # カンマを含む
                    "1.", "1E+", "1E-", "1 ", "1\t")  # 数値以外で終わる

    for (test in test.cases) {
        checkTrue(is.numexp(test))
    }

    # 数値以外の文字列
    test.cases <- c("1234a", "0x123", "1,2,3", "a1234", "12.3,4", "0001,234",
                    "1.234+E02", "1,2345,678", "0,000", ",000", "", "e+02", ".")
    for (test in test.cases) {
        checkTrue(!is.numexp(test))
    }
}

Perlで書くなら次のような感じでしょうか。

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

sub is_numexp {
    my $str = shift;
    $str =~ m{^\s*[-+]?
              (?:
                  # begin with number
                  (?:
                     \d+  # without commas
                     | (?: 0[1-9]{1,2} | 00[1-9] | [1-9]\d{0,2} )(?: ,\d{3} )+  # with commas
                  )
                  (?: \.\d* )?
                  # begin with dot
                  | \.\d+
              )
              (?i: e[-+]\d* )?  # scientific notation
              \\s*$}x;
}

# 数値の文字列(16進数は対象外)
my @test_cases = (
                  '1234', '1234.', '1234.00000', '1234.1234', '01234',
                  '.1234', '-1234', '+1234', ' 1234', "\t1234",  # 数字以外で始まる
                  '1234E+02', '1.234E+', '1.234E+02', '1.234e-2', '.12e+02',  # 指数表記
                  '1,234', '01,234', '12,345,678', '123,456,789', '10,234',  # カンマを含む
                  '1.', '1E+', '1E-', '1 ', "1\t",  # 数値以外で終わる
                 );
foreach my $test (@test_cases) {
    ok(is_numexp($test));
}

# 数値以外の文字列
@test_cases = (
               '1234a', '0x123', '1,2,3', 'a1234', '12.3,4', '0001,234',
               '1.234+E02', '1,2345,678', '0,000', ',000', '', 'e+02', '.'
              );
foreach my $test (@test_cases) {
    ok(!is_numexp($test));
}

done_testing;

どうぞご自由にお使いください!