Рубиновая строка: убегай и убирай пользовательский персонаж - PullRequest
1 голос
/ 29 октября 2011

Предположим, я сказал £ символ как опасный, и я хочу иметь возможность защищать и снимать защиту с любой строки. И наоборот.

Пример 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, экранировать (и не удалять) данный символ (например, £ в моем примере) из строки?

Редактировать: вот объяснение поведения этого вопроса.

Прежде всего, спасибо за ваши ответы. У меня есть приложение Rails с моделью Tweet, имеющей поле 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 </p>

Теперь, если есть это:

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 ]

2 голосов
/ 29 октября 2011

Надеюсь, ваша версия 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 голос
/ 29 октября 2011

Если вы используете 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 != "") или if ($1)), но я знаю, что этот общий метод (использование кода вместо простой строки замены) работает. Недавно я использовал эту же технику для моего решения JavaScript для аналогичного вопроса , который искал звездочки без экранирования.

0 голосов
/ 29 октября 2011

Я не уверен, что это то, что вы хотите, но я думаю, что вы можете сделать простой поиск и замена:

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

Обратите внимание, что я изменил \ на \\, потому что вы должны экранировать обратную косую черту в строке в двойных кавычках.


Редактировать: Я думаю, что вам нужно регулярное выражение, которое соответствует нечетному числу обратных косых черт:

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

Это делает следующие преобразования

"£"       #=> "£"
"\\£"     #=> "£"
"\\\\£"   #=> "\\\\£"
"\\\\\\£" #=> "\\\\£"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...