Может кто-нибудь объяснить денежное регулярное выражение, которое просто проверяет, соответствует ли значение некоторому шаблону? - PullRequest
5 голосов
/ 28 января 2010

Здесь есть несколько сообщений, которые фиксируют значение , но я просто хочу проверить, является ли значение чем-то. Более расплывчато выражено; Я пытаюсь понять разницу между проверкой значения и его «захватом». В текущем случае значением будут следующие допустимые форматы денег:

Вот пост , в котором объясняется кое-что о денежном регулярном выражении, но я его немного не понимаю.

.50
50
50.00
50.0
$5000.00
$.50

Я не хочу запятых (люди должны знать, что это смешно).

У меня проблемы с:

  1. Допускается $ в начале значения (но все равно необязательно)
  2. Допускается только 1 десятичная точка (но не разрешается в конце)
  3. Понимание того, как это работает внутри
  4. Также понимание того, чтобы получить из него нормализованную версию (только цифры и необязательный десятичный знак), которая лишает знак доллара.

Мое текущее регулярное выражение (которое явно не работает правильно):

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

(Примечание: я работаю в Python)

Ответы [ 4 ]

15 голосов
/ 28 января 2010

Если вы хотите разрешить $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* соответствует серии из 0 или более цифр
    • \d+ соответствует серии из одной или нескольких цифр
    • \d{1,2} соответствует любой одной цифре или серии из двух цифр

Заключенные в скобки подшаблоны являются группами захвата: весь текст на входе, соответствующий подвыражению в группе захвата, будет доступен в matchobj.group(index). Знак доллара не будет зафиксирован, потому что он находится за скобками.

Поскольку Python не поддерживает несколько групп захвата с одним и тем же именем (!!!), мы должны искать в matchobj.groups() ту, которая не None. Это также означает, что вы должны быть осторожны при изменении шаблона, чтобы использовать (?:...) для каждой группы, кроме суммы.

Подгоняя хороший тестовый жгут Марка, мы получаем

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)
6 голосов
/ 28 января 2010

Вот регулярное выражение, которое вы можете использовать:

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)
4 голосов
/ 28 января 2010

Также понимание того, чтобы получить из него нормализованную версию (только цифры и дополнительный десятичный знак), которая лишает знак доллара.

Это также известно как "захват" значения;)

Отработка базового примера Аарона:

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

Тогда сумма (без знака доллара) будет в группе захвата 1.

3 голосов
/ 28 января 2010

Я считаю, что следующее регулярное выражение удовлетворит ваши потребности:

/^\$?(\d*(\.\d\d?)?|\d+)$/

Это допускает необязательный '$'. Он допускает необязательное десятичное число, но требует, по крайней мере, одну, но не более двух цифр после десятичного числа, если присутствует десятичное число.

Редактировать: Внешние скобки поймают все числовое значение для вас.

...