Как выполнять замены строк без экранирования обратной косой черты в Ruby - PullRequest
3 голосов
/ 20 мая 2019

Рассмотрим код:

output = `cat test.txt`
puts output  # /^\\([0-3][0-9]\\/[0-1][0-9]\\/[2-9][0-9]{3}\\)$/
str = 'test ' + output
puts str     # test /^\\([0-3][0-9]\\/[0-1][0-9]\\/[2-9][0-9]{3}\\)$/
new_str = 'new test ' + output
puts new_str # new test /^\\([0-3][0-9]\\/[0-1][0-9]\\/[2-9][0-9]{3}\\)$/

res = str.sub('test', 'new test')
puts res     # new test /^\\([0-3][0-9]\\/[0-1][0-9]\\/[2-9][0-9]{3}\\)$/ <-- all fine
res = str.sub(str, new_str)
puts res     # new test /^\([0-3][0-9]\/[0-1][0-9]\/[2-9][0-9]{3}\)$/     <-- !!! problem

код только для того, чтобы представить проблему, которая у меня есть;)

Проблема: У меня есть текст замены с двойной обратной косой чертой, который мне нужно записать "как есть" в другой файл

Вопрос : есть ли какой-нибудь простой метод замены, который не интерпретирует обратную косую черту (возможно, какой-то двоичный режим)?

Потому что довольно странно делать так: res = str.sub(str, new_str.gsub('\\', '\\\\\\\\')), хотя это работает ...

реальный рабочий код:

file = 'some/random/file.php'
contents = new_contents = ''
File.open(file, 'rb') do |f|
  contents = new_contents = f.read
end

contents.scan(/('([A-Z]+)' \=\> \<\<\<'JSON'(.*?)JSON)/m) do |match|
  Dir.glob("*#{match[1]}.json") do |filename|
    compressed = `../compress.py #{filename}`.gsub('\\', '\\\\\\\\')
    replacement = match[0].sub(match[2], "\n" + compressed).force_encoding('ASCII-8BIT')

    new_contents = new_contents.sub(match[0], replacement.gsub('\\', '\\\\\\\\'))
  end
end

File.open(file, 'wb') do |f|
  f.write(new_contents)
end

Ответы [ 3 ]

2 голосов
/ 21 мая 2019

В конце концов я нашел довольно простое решение:

input = '{"regex": "/^\\\\([0-3][0-9]\\\\)$/"}'
puts input # gives => {"regex": "/^\\([0-3][0-9]\\)$/"}

search = '/^\\\\([0-3][0-9]\\\\)$/'
replace = '/^\\\\([0-9]\\\\)$/'

puts input.sub(search, replace) # gives => {"regex": "/^\([0-9]\)$/"}, which is wrong result

input[search] = replace # <-- here is the trick, but makes changes in place
puts input # gives => {"regex": "/^\\([0-9]\\)$/"} => success!

Но! Если ваша строка не содержит подстроки search, вы получите string not matched (IndexError).

Так что вы можете сделать пуленепробиваемый код следующим образом:

input[search] = replace if input.include? search

Кроме того, если вы хотите сохранить input нетронутым, вы можете .dup перевести его в другую переменную:

new_input = input.dup
new_input[search] = replace if new_input.include? search
1 голос
/ 20 мая 2019

Вы можете заменить часть строки, используя диапазон.Так что вам просто нужно найти этот диапазон

if index = string.index(str_to_replace)
  string[index...(index + str_to_replace.length)] = replacement
end

В этом примере это делается на месте, поэтому при необходимости сделайте это с дуплом.

0 голосов
/ 21 мая 2019

Нет, каждый литерал Ruby String должен избегать обратной косой черты.Ruby допускает произвольные строковые разделители , например:

%q{A string}
%Q<A string>
%.A string.

См. ruby-doc.org и wikibooks.org .Тем не менее, все они обрабатывают обратную косую черту как специальный символ и требуют его экранирования.

Один из вариантов - поместить ваши замены в файл данных (например, CSV) и прочитать замены следующим образом:

while line = data_file.gets
  pattern, replacement = *line.split(separator)
  str.gsub!(Regexp.compile(pattern), replacement)
end

Файл данных не должен экранировать любые символы при замене.

...