如何使用正则表达式在ruby中交换字符串中的数字位置?

时间:2015-01-24 00:21:26

标签: ruby regex

假设我有一个字符串"2foo9 8bar5"。我需要交换包裹每个单词的两个数字。结果应如下"9foo2 5bar8"

我可以使用以下代码完成。

def swap_num(str)
  str = str.gsub(/(\d)(\D+)(\d)/, '\3\2\1')
end

但如果我有一个像"2foo9 8bar5 3+=_1"这样的字符串,这个swap_num方法也会交换最后一部分"3+=_1"的数字,这不是我想要的。我只想交换包装WORDS的数字,而不是任何字符。

我尝试了以下方法,但没有奏效。

def swap_num(str)
  str = str.gsub(/(\d)([a-zA-Z]+)(\d)/, '\3\2\1')
end

用简单的正则表达式做任何事情?谢谢!

更新

对不起伙计们。我犯了一个错误,str = str.gsub(/(\d)([a-zA-Z]+)(\d)/, '\3\2\1')实际上是我的目的。但如果我使用像"\3\2\1"这样的双引号,它就行不通。

谢谢,@ Robin指出我的原始代码确实有效。还要感谢@Cary Swoveland和@ Andie2302为我提供2个新的解决方案!真的很感激!

2 个答案:

答案 0 :(得分:6)

你可以这样做:

R = /
    \b       # match a word boundary
    (\d+)    # match ?= 1 digits, capture in group #1
    ([a-z]+) # match >= 1 lower case letters, capture in group #2
    (\d+)    # match >= 1 digits, capture in group #3
    \b       # match a word boundary
    /ix      # case indifferent (/i) and extended mode (/x)

def flip_numbers(str)
  str.gsub(R) { $3+$2+$1 }
end

flip_numbers("2foo9 8bar5")
  #=> "9foo2 5bar8"
flip_numbers("233foo91 8bar5")
  #=> "91foo233 5bar8" 
flip_numbers("2foo9 8bar5 3+=_1")
  #=> "9foo2 5bar8 3+=_1"
flip_numbers("a2foo9 8bar5 3bat7c")
  #=> "a2foo9 5bar8 3bat7c"

请注意,在上一个示例中,由于字边界要求,a2foo93bat7c中的数字不会被交换。

每个匹配的字符串都传递给gsub的块,该块计算该字符串的替换。我们可以把块写成:

{ |s| <code here> }

其中block变量等于匹配的字符串。在第一个例子中:

s = "2foo9"

我们希望将其替换为:

"9foo2"

三个捕获组的内容包含在全局变量中:

$1 #=> "9"
$2 #=> "foo"
$3 #=> "2"

因此替换字符串是:

$3+$2+$1 #=> "2foo9".

因此写了块:

{ |s| $3+$2+$1 }

但由于我们在此计算中不使用块变量s,因此我们可以从块中省略|s|。这是一种很好的做法,因为它减少了出错的可能性,并告诉读者不使用块变量。

表达式$3+$2+$1可以改为gsub的参数,但在这种情况下必须写成:

"2foo9 8bar5".gsub(R, '\3\2\1')
  #=> "9foo2 5bar8" 

"2foo9 8bar5".gsub(R, "\\3\\2\\1")
  #=> "9foo2 5bar8" 

这里的选择纯粹是风格。

答案 1 :(得分:4)

工作正则表达式是:

(\d)(\p{L}+)(\d)

str = str.gsub(/(\d)(\p{L}+)(\d)/, '\3\2\1')

\ p {L} ...匹配Unicode类别“字母”中的字符(任何语言的任何字母字符)