Ruby:子匹配组在不应该的时候是零

时间:2015-07-08 15:31:25

标签: ruby regex gsub

我正在尝试获取信用卡号码字符串并用星号掩盖中间数字。 我使用正则表达式/^([\d]{6})([\d]{3,6})([\d]{4})$/来匹配三个组:

  • 前6位数字
  • 将被混淆的中间数字(3到6)
  • 最后4位数

正如您在下面的控制台中看到的那样,gsub匹配组最初是nil,直到我在替换属性中使用文字字符串。然后,当我尝试运行初始调用时,它可以工作。

2.0.0-p598 :001 > c = "5454545454545454"
 => "5454545454545454"

2.0.0-p598 :002 > c.gsub(/^([\d]{6})([\d]{3,6})([\d]{4})$/, $1 + '*' * $2.size + $3)
NoMethodError: undefined method `size' for nil:NilClass
    from (irb):2
    from /Users/guilherme/.rvm/rubies/ruby-2.0.0-p598/bin/irb:12:in `<main>'

2.0.0-p598 :003 > c.gsub(/^([\d]{6})([\d]{3,6})([\d]{4})$/, "anything")
 => "anything"

2.0.0-p598 :004 > c.gsub(/^([\d]{6})([\d]{3,6})([\d]{4})$/, $1 + '*' * $2.size + $3)
 => "545454******5454"

为什么会这样?这是一个错误吗?如何解决这个问题,并在第一次尝试时使其工作?

4 个答案:

答案 0 :(得分:5)

For more advanced substitutions such as this one, you should use the block form of gsub. Magic variables have values there.

s = '1234567890123456'
rgx = /^([\d]{6})([\d]{3,6})([\d]{4})$/

s2 = s.gsub(rgx) do
  $1 + '*' * $2.size + $3
end

s2 # => "123456******3456"

(in the non-block form matches are available as \1, \2 and so on. Good for simple substitutions, but you can't call .size on them, etc.)

Magic variable are not available in the argument form, because arguments are evaluated before invocation of the method. So, variables haven't been set yet. And if you were to call such a method twice, the second time you'll get matches from the first one.

Alternatively, you can avoid all this regex voodoo and just mask characters directly

s3 = s.dup # don't touch the original string
s3[6...-4] = '*' * (s.length - 4 - 6) # string of asterisks of variable length
s3  # => "123456******3456"

答案 1 :(得分:1)

If you can replace characters in the number, I definitely would go for the solution Sergio Tulentsev suggested. If not :

c[0..5] + '*' * (c.size - 10) + c[-4..-1]

答案 2 :(得分:1)

您可以使用sub而不使用捕获组或块,如下所示:

r = /
    (?<=^.{6}) # match any 6 chars at beginning of string in a positive lookbehind
    .+         # match any number of any character, greedily
    (?=.{4}$)  # match any 4 chars at end of string in a positive lookahead
    /x         # extended option

s.sub(r, '*'*(s.size-10))
  #=> "123456******3456" 

答案 3 :(得分:1)

你可以做到这一点,而不会倍增&#39; *&#39;字符串大小。

'1234123412341234'.gsub(/(?<=......).(?=....)/, '*')

=> "123412******1234"