Ruby的字符串:Escape和unescape自定义字符

时间:2011-10-28 23:14:46

标签: ruby regex string escaping

假设我说£字符是危险的,我希望能够保护和取消保护任何字符串。反之亦然。

示例1:

"Foobar £ foobar foobar foobar."  # => dangerous string
"Foobar \£ foobar foobar foobar." # => protected string

示例2:

"Foobar £ foobar £££££££foobar foobar."         # => dangerous string
"Foobar \£ foobar \£\£\£\£\£\£\£foobar foobar." # => protected string

示例3:

"Foobar \£ foobar \\£££££££foobar foobar."        # => dangerous string
"Foobar \£ foobar \\\£\£\£\£\£\£\£foobar foobar." # => protected string

使用Ruby有一种简单的方法可以从字符串中逃避(和unescape)给定字符(例如我的示例中的£)吗?

编辑:这是对此问题行为的解释。

首先,感谢您的回答。我有一个带有Tweet模型的Rails应用,其中content字段。推文示例:

tweet = Tweet.create(content: "Hello @bob")

在模型中,有一个序列化过程可以转换字符串,如下所示:

dump('Hello @bob') # => '["Hello £", 42]'
                   # ... where 42 is the id of bob username

然后,我能够反序列化并显示它的推文:

load('["Hello £", 42]') # => 'Hello @bob'

同样,使用多个用户名也可以这样做:

dump('Hello @bob and @joe!')        # => '["Hello £ and £!", 42, 185]'
load('["Hello £ and £!", 42, 185]') # => 'Hello @bob and @joe!'

这就是目标:)

但是这种发现和替换可能难以执行,例如:

tweet = Tweet.create(content: "£ Hello @bob")

'因为这里我们还必须逃脱£ char。我认为你的解决方案对此有好处。结果就是:

dump('£ Hello @bob')       # => '["\£ Hello £", 42]'
load('["\£ Hello £", 42]') # => '£ Hello @bob'

完美。 < 3< 3

现在,如果有:

tweet = Tweet.create(content: "\£ Hello @bob")

我认为我们首先应该逃避每个\,然后逐出£,例如:

dump('\£ Hello @bob')       # => '["\\£ Hello £", 42]'
load('["\\£ Hello £", 42]') # => '£ Hello @bob'

然而......在这种情况下我们该怎么做:

tweet = Tweet.create(content: "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\£ Hello @bob")

...其中tweet.content.gsub(/(?<!\\)(?=(?:\\\\)*£)/, "\\")似乎无效。

3 个答案:

答案 0 :(得分:2)

希望您的ruby版本支持lookbehinds。如果不是,我的解决方案对你不起作用。

转义字符:

str = str.gsub(/(?<!\\)(?=(?:\\\\)*£)/, "\\")

取消转义字符:

str = str.gsub(/(?<!\\)((?:\\\\)*)\\£/, "\1£")

无论反斜杠的数量如何,两个正则表达式都能正常工作。它们相互补充。

逃脱解释:

"
(?<!        # Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind)
   \\          # Match the character “\” literally
)
(?=         # Assert that the regex below can be matched, starting at this position (positive lookahead)
   (?:         # Match the regular expression below
      \\          # Match the character “\” literally
      \\          # Match the character “\” literally
   )*          # Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
   £           # Match the character “£” literally
)
"

不是说我匹配某个位置。根本不消耗任何文本。当我确定我想要的位置时,我插入一个\。

unescape的说明:

"
(?<!        # Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind)
   \\          # Match the character “\” literally
)
(           # Match the regular expression below and capture its match into backreference number 1
   (?:         # Match the regular expression below
      \\          # Match the character “\” literally
      \\          # Match the character “\” literally
   )*          # Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
)
\\          # Match the character “\” literally
£           # Match the character “£” literally
"

这里我保存所有反斜杠减去一个并且我用特殊字符替换这个反斜杠数。棘手的东西:)

答案 1 :(得分:1)

如果你使用的是Ruby 1.9,那么看起来很糟糕,那么FailedDev的答案应该会很好用。如果你使用的是Ruby 1.8,它没有看起来(我认为),可能会有不同的方法。试一试:

text.gsub!(/(\\.)|£)/m) do
    if ($1 != nil)  # If escaped anything
        "$1"        # replace with self.
    else            # Otherwise escape the
        "\\£"       # unescaped £.
    end
end

请注意,我不是Ruby程序员,并且此代码段未经测试(特别是我不确定:if ($1 != nil)语句用法是否正确 - 可能需要:if ($1 != "")或{ {1}}),但我知道这种通用技术(使用代码代替简单的替换字符串)可行。我最近使用同样的技术my JavaScript solution to a similar question寻找未转义的星号。

答案 2 :(得分:0)

我不确定这是否是您想要的,但我认为您可以进行简单的查找和替换:

str = str.gsub("£", "\\£") # to escape
str = str.gsub("\\£", "£") # to unescape

请注意,我已将\更改为\\,因为您必须使用双引号字符串转义反斜杠。


编辑:我认为你想要的是一个匹配奇数反斜杠的正则表达式:

str = str.gsub(/(^|[^\\])((?:\\\\)*)\\£/, "\\1\\2£")

执行以下转换

"£"       #=> "£"
"\\£"     #=> "£"
"\\\\£"   #=> "\\\\£"
"\\\\\\£" #=> "\\\\£"