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

如何在ruby中使用正则表达式交换字符串中的数字位置?,ruby,regex,Ruby,Regex,假设我有一个字符串“2foo9 8bar5”。我需要交换包裹每个单词的两个数字。结果应该是这样的“9foo2 5bar8” 我可以用下面的代码来完成 def swap_num(str) str = str.gsub(/(\d)(\D+)(\d)/, '\3\2\1') end 但是如果我有一个像这样的字符串,这个方法也会交换最后一部分的数字,这不是我想要的。我只想交换包含单词的数字,而不是任何字符 我尝试了以下方法,但没有成功 def swap_num(str) str = str.g

假设我有一个字符串
“2foo9 8bar5”
。我需要交换包裹每个单词的两个数字。结果应该是这样的
“9foo2 5bar8”

我可以用下面的代码来完成

def swap_num(str)
  str = str.gsub(/(\d)(\D+)(\d)/, '\3\2\1')
end
但是如果我有一个像这样的字符串,这个方法也会交换最后一部分的数字,这不是我想要的。我只想交换包含单词的数字,而不是任何字符

我尝试了以下方法,但没有成功

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为我提供了两个新的解决方案!真的很感激

工作正则表达式是:

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

str = str.gsub(/(\d)(\p{L}+)(\d)/, '\3\2\1')
\p{L}。。。匹配Unicode类别“字母”(任何语言的任何字母字符)中的字符。

您可以执行以下操作:

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"
注意,在最后一个示例中,由于单词边界要求,
a2foo9
3bat7c
中的数字不交换

每个匹配的字符串被传递到
gsub
的块,该块计算该字符串的替换。我们可以将块写为:

{ |s| <code here> }
我们希望用以下内容取代:

"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" 


这里的选择纯粹是风格上的。

您的尝试怎么会失败?它根本不会改变任何东西,并且输出与我输入的字符串完全相同的字符串。对我来说,这似乎没什么问题:您能显示更多代码吗?我的错。实际上,这项工作。当我使用双引号
“\3\2\1”
时,它不起作用。然后它输出这个结果
“\u0003\u0002\u0001\u0003\u0002\u0001 3+=\u1”
。但是,当我使用单引号时,它是有效的。这是为什么?实际上我刚刚找到了我的原始代码
str=str.gsub(/(\d)([a-zA-Z]+)(\d/,“\3\2\1”)
将实现我所期望的,这只是在包装
[a-zA-Z]+
时交换数字。我没有得到正确的输出,因为我使用了双引号,比如
“\3\2\1”
,而不是单引号。你能解释一下
\p{L}
的作用吗?埃德蒙,假设你试着匹配单词
str=“Passé”
的字母。让我们先试试
r=/[a-zA-Z]+/
str[r]#=>“Pass”
。不太好。我们需要一个能处理Unicode字符的正则表达式。我们可以使用
/\p{L}+/
/\p{alpha}+/
/[:alpha:]+/
。这些都返回“Passé”。然而,当匹配预期仅为ASCII字符时,有些人更喜欢其中任何一个字符的简洁性/灵活性,而不是
/[a-zA-Z]+/
(或
/[a-Z]+//i
)。请参阅已接受的答案进行更全面的讨论。
\p{L}
在本例中只是混淆了问题。这个问题与unicode无关,所以没有理由这样做。@pguardiario unicode从来没有错过。它符合交换号码包装文字的要求。谢谢@Cary Swoveland。但是你能解释一下块
{$3+$2+$1}
是如何工作的吗?我是新来的,以前从未使用过美元符号。我提供了一个解释。
"2foo9 8bar5".gsub(R, '\3\2\1')
  #=> "9foo2 5bar8" 
"2foo9 8bar5".gsub(R, "\\3\\2\\1")
  #=> "9foo2 5bar8"