【问题标题】:How to perform String replacements without backslash escaping in Ruby如何在没有反斜杠转义的情况下在 Ruby 中执行字符串替换
【发布时间】:2019-10-07 00:00:20
【问题描述】:

考虑代码:

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

【问题讨论】:

    标签: ruby string replace escaping


    【解决方案1】:

    最终我找到了非常简单的解决方案:

    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
    

    【讨论】:

    • 我看到了你的问题。 String#sub 替换字符串可以有对匹配部分的反斜杠引用,例如。 "\1 \2",因此需要对反斜杠进行转义。
    • 没错!但我不需要任何反向引用,只是简单的替换:)
    【解决方案2】:

    您可以使用范围替换字符串的一部分。所以你只需要找到那个范围

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

    这个例子是在原地做的,所以如果需要的话,做一个 dup。

    【讨论】:

    • 好一个!但我想出了更简单的解决方案;)检查我的答案
    【解决方案3】:

    不,每个 Ruby 字符串文字都必须转义反斜杠。 Ruby 确实允许像这样的任意字符串 分隔符

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

    ruby-doc.orgwikibooks.org。但是,这些都将反斜杠视为特殊字符并要求对其进行转义。

    一种选择是将替换项放入数据文件(例如 CSV)中,然后像这样读取替换项:

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

    数据文件将不需要在替换中转义任何字符。

    【讨论】:

    • 如果您仔细查看我的真实代码,您会发现我已经从外部资源接收替换,因此它不是“双引号字符串”或其他任何内容。问题出在sub/gsub 方法中,他们试图解释双反斜杠,结果替换后我只有一个反斜杠
    猜你喜欢
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 2011-07-15
    • 2011-08-01
    • 1970-01-01
    • 2018-02-27
    • 2017-01-17
    相关资源
    最近更新 更多