Давайте посмотрим на ваше регулярное выражение, которое я напишу в режиме свободного пробела , чтобы я мог его документировать:
r = /
\$ # match a dollar sign
\ ? # optionally match a space (has no effect)
( # begin capture group 1
\d+ # match one or more digits
, # match a comma (need not be escaped)
)* # end capture group 1 and execute it >= 0 times
\d+ # match one or more digits
\.? # optionally match a period
\d* # match zero or more digits
/x # free-spacing regex definition mode
В режиме без свободного пробела это будетзаписывается следующим образом.
r = /\$ ?(\d+,)*\d+\.?\d*/
Когда регулярное выражение определено в режиме свободного пробела, все пробелы удаляются перед его вычислением, поэтому мне пришлось покинуть пространство.В этом нет необходимости, если регулярное выражение не определено в режиме свободного пространства.
Не нужно совпадать с пробелом после знака доллара, поэтому \ ?
следует удалить.Предположим, теперь у нас есть
r = /\$\d+\.?\d*/
"$2.31 cat $44. dog $33.607".scan r
#=> ["$2.31", "$44.", "$33.607"]
Это работает, но сомнительно, хотите ли вы сопоставить значения, которые не имеют ровно две цифры после десятичной точки.
Теперь напишите
r = /\$(\d+,)*\d+\.?\d*/
"$2.31 cat $44. dog $33.607".scan r
#=> [[nil], [nil], [nil]]
Чтобы понять, почему был получен этот результат, изучите документ для String # scan , в частности, последнее предложение первого абзаца: «Если шаблон содержит группы, каждый отдельный результат сам является массивом, содержащимодна запись на группу.
Мы можем избежать этой проблемы, изменив группу захвата на группу без захвата:
r = /\$(?:\d+,)*\d+\.?\d*/
"$2.31 cat $44. dog $33.607".scan r
#=> ["$2.31", "$44.", "$33.607"]
Теперь рассмотрим это:
"$2,241.31 cat $1,2345. dog $33.607".scan r
#=> ["$2,241.31", "$1,2345.", "$33.607"]
, что до сих пор не совсемправо.Попробуйте следующее.
r = /
\$ # match a dollar sign
\d{1,3} # match one to three digits
(?:,\d{3}) # match ',' then 3 digits in a nc group
* # execute the above nc group >=0 times
(?:\.\d{2}) # match '.' then 2 digits in a nc group
? # optionally match the above nc group
(?![\d,.]) # no following digit, ',' or '.'
/x # free-spacing regex definition mode
"$2,241.31 $2 $1,234 $3,6152 $33.607 $146.27".scan r
#=> ["$2,241.31", "$2", "$1,234", "$146.27"]
(?![\d,.])
является негативным прогнозом .
В обычном режиме это регулярное выражение записывается следующим образом.
r = /\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?(?![\d,.])/
Следующий ошибочный результат будет получен без отрицательного просмотра в конце регулярного выражения.
r = /\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?/
"$2,241.31 $2 $1,234 $3,6152 $33.607 $146.27".scan r
#=> ["$2,241.31", "$2", "$1,234", "$3,615", "$33.60",
# "$146.27"]