【问题标题】:How do I replace words in a string based on words in an Array in Ruby?如何根据 Ruby 中数组中的单词替换字符串中的单词?
【发布时间】:2016-10-20 09:21:17
【问题描述】:

我将如何执行以下操作?我尝试执行此 gsub,但如果 strings_to_highlight 数组很大,我无法弄清楚什么是真正有效的。干杯!

  string = "Roses are red, violets are blue"

  strings_to_highlight = ['red', 'blue']

  # ALGORITHM HERE

  resulting_string = "Roses are (red), violets are (blue)"

【问题讨论】:

标签: ruby-on-rails ruby


【解决方案1】:

Regexp 有一个有用的union 函数用于将正则表达式组合在一起。坚持使用正则表达式,直到出现性能问题:

string = "Roses are red, violets are blue"
strings_to_highlight = ['red', 'blue']

def highlight(str, words)
  matcher = Regexp.union words.map { |w| /\b(#{Regexp.escape(w)})\b/ }
  str.gsub(matcher) { |word| "(#{word})" }
end

puts highlight(string, strings_to_highlight)

【讨论】:

  • 在这里很好地使用了Regexp.union,但请尝试链接到当前版本的文档,并使用[...](...) 符号来避免丑陋的裸URL。
  • 小心Regexp.union words.map { |w| /\b(#{Regexp.escape(w)})\b/ } # => /(?-mix:\b(red)\b)|(?-mix:\b(blue)\b)/。生成的模式中存在潜在的缺陷。另外,生成的模式可能要简单得多。
  • @theTinMan 有什么陷阱?生成的正则表达式过于复杂,因为它保留了所有组件模式的选项(?-mix 意味着每个子模式都没有启用 m、i 和 x),但我仍然宁愿使用 Regexp#union用正则表达式字符串插值和文字“|”构建一个大模式。
【解决方案2】:
strings_to_highlight = ['red', 'blue']
string = "Roses are red, violets are blue"

strings_to_highlight.each { |i| string.gsub!(/\b#{i}\b/, "(#{i})")}

【讨论】:

  • string = 'redeem' 时会发生什么?
  • 试一试,但我认为你会得到 (red)eem
  • @CdotStrifeVII 请修正您的答案,使其按要求工作。
  • 这可行,但在较长的数组和较长的字符串上会几何上变慢。
  • 嘿@sagarpandya82 因此,据我所知,所请求的功能是一种算法,它替换字符串中与数组中的字符串匹配的子字符串,而我的解决方案就是这样做的。你指的是什么?
【解决方案3】:

我建议使用String#gsub 的形式,它使用哈希进行替换。

strings_to_highlight = ['red', 'blue']

首先构造哈希。

h = strings_to_highlight.each_with_object({}) do |s,h|
  h[s] = "(#{s})"
  ss = "#{s[0].swapcase}#{s[1..-1]}"
  h[ss] = "(#{ss})"
end
  #=> {"red"=>"(red)", "Red"=>"(Red)", "Blue"=>"(Blue)", "blue"=>"(blue)"} 

接下来为它定义一个默认的proc:

h.default_proc = ->(h,k) { k }

如果h 没有密钥kh[k] 返回k(例如h["cat"] #=> "cat")。

准备好了!

string = "Roses are Red, violets are blue"

string.gsub(/[[[:alpha:]]]+/, h)
 => "Roses are (Red), violets are (blue)"

这应该是相对有效的,因为只需要一次遍历字符串并且哈希查找非常快。

【讨论】:

    【解决方案4】:

    我会使用:

    string = "Roses are red, violets are blue"
    strings_to_highlight = ['red', 'blue']
    
    string.gsub(/\b(#{Regexp.union(strings_to_highlight).source})\b/) { |s| "(#{s})" } # => "Roses are (red), violets are (blue)"
    

    下面是它的分解方式:

    /\b(#{Regexp.union(strings_to_highlight).source})\b/ # => /\b(red|blue)\b/
    

    在嵌入模式时使用source 很重要。没有它会导致:

    /\b(#{Regexp.union(strings_to_highlight)})\b/ # => /\b((?-mix:red|blue))\b/
    

    如果您不理解 regex-ese 中的含义,那么 (?-mix:...) 部分可能会导致问题。 Regexp 文档解释了这些标志,但如果您不知道该问题,则不这样做可能会导致非常难以诊断错误。

    \b 告诉引擎匹配单词,而不是子字符串。没有它,你可能会得到:

    string = "Fred, bluette"
    strings_to_highlight = ['red', 'blue']
    string.gsub(/(#{Regexp.union(strings_to_highlight).source})/) { |s| "(#{s})" } 
    # => "F(red), (blue)tte"
    

    使用带有gsub 的块允许我们对匹配的值执行计算。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-03
      • 2021-12-07
      • 2012-09-13
      • 2015-07-03
      相关资源
      最近更新 更多