仰るような動作をさせたいのであれば、御呈示の正規表現に対して以下 3 点の修正が必要かと存じます。
1) 0 文字ではマッチしないよう、パターンに最低 1 文字の制限を入れる
現状のパターンでは、パターン全体が 空の文字列に対しても真となる ため、一つ結果が得られる度、続く 0 文字にマッチして ('', '')
という値が得られてしまっています。少なくとも 1 文字以上の数字が必須であることを示すため、 \d{,3}
を \d{1,3}
へと変更したほうが良いでしょう。
2) 数字の一部を取り出してしまわないよう、区切り文字を明示する
現状のパターンでは、 どこからどこまでがひとまとまりの数字か が明示されていないため、数字の 一部分でもパターンに一致してしまえば正しいと見做されてしまう 状態です。その結果、 12,34,567
という値が 12
と 34,567
の二つに分ければ OK と判断され取り出されてしまっています。
具体的には、例えばパターン先頭に (?:^| )
(文字列先頭または半角スペースに一致) を、パターン末尾に (?=$| )
(文字列末尾または半角スペースが一文字先に存在) を入れると良いのではないでしょうか。
※ここでパターン末尾で肯定先読みを使っているのは、単純にマッチさせてしまうと、次の数字のまとまりの判定時、先頭に期待する半角スペースを既に前のマッチで消費済みとして見失ってしまうためです
3) パターン繰り返しの括弧と、取り出しの括弧を分ける
6,368,745
の第二グループが ,745
になっているのは、パターンを繰り返すための括弧をそのまま取り出している為です。実際、御書きのパターンでは 括弧の中が ,\d{3}
となっているわけですから、これにマッチするのは ,368
または ,745
のどちらかになる 、と申し上げれば御分り頂けますでしょうか。このうち最後にマッチした,745
が結果として返ってきています。
この部分は、本当に取り出したいのが パターンを繰り返した後の全体なのであれば、繰り返しを含めて括弧で囲んで取り出す 書き方としましょう。つまり、 (,\d{3})*
となっている部分を ((?:,\d{3})*)
として、アスタリスクまでを含んだ部分を囲ってやれば良いかと思います。
x) 無理な「正規表現一発」に拘らない
以上の修正を加えると、パターンは (?:^| )(\d{1,3}((?:,\d{3})*))(?=$| )
という形になり、これで恐らく希望の動作をしてくれるはずです。
但し、修正 2 点目で述べた通り、 結局のところ数字と数字の区切りはどこかという情報が必要 なのですから、無理につながった状態で正規表現に掛けようとせず、その区切りで 分けた後に一つ一つ調べていった方が簡単で分かりやすい ように思います。例えば、次のようなコードにしてやれば、あまり正規表現の複雑さに悩まず必要な数字だけを取り出すことが可能です。
python
1mo = [
2 num for num in text.split(' ')
3 if re.match(r'^\d{1,3}(,\d{3})*$', num)
4]
仮に数字の区切りが正規表現でなければ扱えないほどに複雑であったとしても、「数字を分ける正規表現」と「正しさを判定する正規表現」との 2 回に分けた方が簡単です。特別な理由のない限り、単純なやり方を組み合わせることを推奨致します。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。