Consider the code:
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
code is just for presenting the problem which I have ;)
Problem: I have replacement text with double backslashes which I need to write "as it is" to another file
Question is: is there any simple replacement method which doesn't interpret backslashes (maybe some binary mode)?
Because it's kinda weird to do like this: res = str.sub(str, new_str.gsub('\\', '\\\\\\\\')), although this works...
real working code:
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
Eventually I found pretty simple solution:
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!
But! If your string doesn't contain any search substring you will get an string not matched (IndexError).
So you may want to bulletproof your code like this:
input[search] = replace if input.include? search
Also, if you want to keep your input untouched you may .dup it to another variable:
new_input = input.dup
new_input[search] = replace if new_input.include? search
You can replace part of a string using a range. So you just have to find that range
if index = string.index(str_to_replace)
string[index...(index + str_to_replace.length)] = replacement
end
This example does it in place, so do it to a dup if necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With