有人能解释一下只检查值是否匹配某个模式的金钱正则表达式吗?

6 投票
4 回答
14831 浏览
提问于 2025-04-15 18:34

这里有很多帖子讨论如何处理数值,但我只是想确认一下这个数值到底是什么。简单来说,我想弄清楚检查一个数值和“捕获”一个数值之间的区别。在这个情况下,数值指的是一些可以接受的货币格式:

这里有一篇帖子,里面解释了一些关于货币正则表达式的内容,但我一点也不明白。

.50
50
50.00
50.0
$5000.00
$.50

我不想要逗号(大家应该知道这很荒谬)。

我遇到的问题是:

  1. 允许在数值开头有一个$符号(但这个符号是可选的)
  2. 只允许有一个小数点(但不允许小数点在最后)
  3. 理解它是如何工作的
  4. 还想知道如何得到一个标准化的版本(只包含数字和可选的小数点),并去掉美元符号。

我现在的正则表达式(显然不太对)是:

# I'm checking the Boolean of the following:
re.compile(r'^[\$][\d\.]$').search(value)

(注意:我在使用Python)

4 个回答

4

还需要弄明白如何从中提取一个标准化的版本(只包含数字和可选的小数点),并去掉美元符号。

这也被称为“捕获”这个数值;)

基于Aaron的基本例子:

/^\$?(\d+(?:\.\d{1,2})?)$/

那么去掉美元符号后的金额会在捕获组1中。

8

这里有一个你可以使用的正则表达式:

regex = re.compile(r'^\$?(\d*(\d\.?|\.\d{1,2}))$')

这是我用来测试这个正则表达式的测试环境。我把你所有的测试都放进来了,还有我自己加的一些测试。我也加入了一些负面测试,因为确保它在不应该匹配的时候确实不匹配,和确保它在应该匹配的时候确实匹配一样重要。

tests = [
    ('.50', True),
    ('50', True),
    ('50.00', True),
    ('50.0', True),
    ('$5000', True),
    ('$.50', True),
    ('$5.', True),
    ('$5.000', False),
    ('5000$', False),
    ('$5.00$', False),
    ('$-5.00', False),
    ('$5,00', False),
    ('', False),
    ('$', False),
    ('.', False),
]

import re
regex = re.compile(r'^\$?(\d*(\d\.?|\.\d{1,2}))$')
for test, expected in tests:
    result = regex.match(test) 
    is_match = result is not None
    print test + '\t' + ('OK' if is_match == expected else 'Fail')

如果你想得到没有$符号的值,可以使用捕获组:

print result.group(1)
17

假设你想允许 $5. 这种格式,但不想允许 5.,那么下面的内容可以帮助你理解如何设置你的规则:

money = re.compile('|'.join([
  r'^\$?(\d*\.\d{1,2})$',  # e.g., $.50, .50, $1.50, $.5, .5
  r'^\$?(\d+)$',           # e.g., $500, $5, 500, 5
  r'^\$(\d+\.?)$',         # e.g., $5.
]))

这里有几个重要的点需要明白:

  • ^$ 分别表示输入字符串的开始和结束。
  • \. 表示一个字面上的点(小数点)。
  • \$ 表示一个字面上的美元符号。
    • \$? 表示一个美元符号或者什么都没有(也就是说,美元符号是可选的)。
  • \d 表示任何一个数字(0-9)。
    • \d* 表示零个或多个数字。
    • \d+ 表示一个或多个数字。
    • \d{1,2} 表示一个数字或者两个数字。

括号中的子模式是捕获组:输入中与捕获组匹配的所有文本都可以通过 matchobj.group(index) 来获取。美元符号不会被捕获,因为它在括号外面。

因为 Python 不支持多个同名的捕获组(!!!),所以我们必须在 matchobj.groups() 中查找那个不是 None 的组。这也意味着在修改模式时,你需要小心,除了金额以外的每个组都要使用 (?:...)

调整 Mark 的测试工具后,我们得到了:

for test, expected in tests:
    result = money.match(test) 
    is_match = result is not None
    if is_match == expected:
      status = 'OK'
      if result:
        amt = [x for x in result.groups() if x is not None].pop()
        status += ' (%s)' % amt
    else:
      status = 'Fail'
    print test + '\t' + status

输出结果:

.50     OK (.50)
50      OK (50)
50.00   OK (50.00)
50.0    OK (50.0)
$5000   OK (5000)
$.50    OK (.50)
$5.     OK (5.)
5.      OK
$5.000  OK
5000$   OK
$5.00$  OK
$-5.00  OK
$5,00   OK
        OK
$       OK
.       OK
.5      OK (.5)

撰写回答