【问题标题】:Splitting string in Ruby on list of words using regex使用正则表达式在单词列表上拆分 Ruby 中的字符串
【发布时间】:2013-06-12 18:21:03
【问题描述】:

我正在尝试根据停用词列表将 Ruby 中的字符串拆分为更小的子字符串或短语。 split 方法在我直接定义正则表达式模式时起作用;但是,当我尝试通过在 split 方法本身内进行评估来定义模式时,它不起作用。

在实践中,我想读取一个包含停用词的外部文件并用它来拆分我的句子。所以,我希望能够从外部文件构造模式,而不是直接指定它。我还注意到,当我使用“pp”和“puts”时,我的行为非常不同,我不知道为什么。我在 Windows 上使用 Ruby 2.0 和 Notepad++。

 require 'pp'
 str = "The force be with you."     
 pp str.split(/(?:\bthe\b|\bwith\b)/i)
 => ["", " force be ", " you."]
 pp str.split(/(?:\bthe\b|\bwith\b)/i).collect(&:strip).reject(&:empty?)
 => ["force be", "you."] 

上面的最终数组是我想要的结果。但是,这在下面不起作用:

 require 'pp'
 stop_array = ["the", "with"]
 str = "The force be with you." 
 pattern = "(?:" + stop_array.map{|i| "\b#{i}\b" }.join("|") + ")"
 puts pattern
 => (?thwit)
 puts str.split(/#{pattern}/i)
 => The force be with you.
 pp pattern
 => "(?:\bthe\b|\bwith\b)"
 pp str.split(/#{pattern}/i)
 => ["The force be with you."]

更新:使用下面的 cmets,我修改了我的原始脚本。我还创建了一种拆分字符串的方法。

 require 'pp'

 class String
      def splitstop(stopwords=[])
      stopwords_regex = /\b(?:#{ Regexp.union(*stopwords).source })\b/i
      return split(stopwords_regex).collect(&:strip).reject(&:empty?)
      end
 end

 stop_array = ["the", "with", "over"]

 pp "The force be with you.".splitstop stop_array
 => ["force be", "you."]
 pp "The quick brown fox jumps over the lazy dog.".splitstop stop_array
 => ["quick brown fox jumps", "lazy dog."]

【问题讨论】:

  • /(?:\bthe\b|\bwith\b)/ 最好写成/\b(?:the|with)\b/

标签: ruby regex string


【解决方案1】:

我会这样做:

str = "The force be with you."     
stop_array = %w[the with]
stopwords_regex = /(?:#{ Regexp.union(stop_array).source })/i
str.split(stopwords_regex).map(&:strip) # => ["", "force be", "you."]

使用Regexp.union 时,请务必注意生成的实际模式:

/(?:#{ Regexp.union(stop_array) })/i
=> /(?:(?-mix:the|with))/i

嵌入的(?-mix: 关闭了模式内部不区分大小写的标志,这会破坏模式,导致它抓取错误的东西。相反,您必须告诉引擎只返回模式,不带标志:

/(?:#{ Regexp.union(stop_array).source })/i
=> /(?:the|with)/i

这就是pattern = "(?:\bthe\b|\bwith\b)" 不起作用的原因:

/#{pattern}/i # => /(?:\x08the\x08|\x08with\x08)/i

Ruby 将"\b" 视为退格字符。而是使用:

pattern = "(?:\\bthe\\b|\\bwith\\b)"
/#{pattern}/i # => /(?:\bthe\b|\bwith\b)/i

【讨论】:

    【解决方案2】:
    stop_array = ["the", "with"]
    re = Regexp.union(stop_array.map{|w| /\s*\b#{Regexp.escape(w)}\b\s*/i})
    
    "The force be with you.".split(re) # =>
    [
      "",
      "force be",
      "you."
    ]
    

    【讨论】:

      【解决方案3】:

      你必须掩盖反斜杠:

      "\\b#{i}\\b" 
      

      pattern = "(?:" + stop_array.map{|i| "\\b#{i}\\b" }.join("|") + ")"
      

      还有一个小的改进/简化:

      pattern = "\\b(?:" + stop_array.join("|") + ")\\b"
      

      然后:

      str.split(/#{pattern}/i) # => ["", " force be ", " you."]
      

      如果您的停止清单很短,我认为这是正确的方法。

      【讨论】:

      • 使用结果模式,展示这将如何解决 OP 的问题。
      【解决方案4】:
      s = "the force be with you."
      stop_words = %w|the with is|
      # dynamically create a case-insensitive regexp
      regexp = Regexp.new stop_words.join('|'), true
      result = []
      while(match = regexp.match(s))
        word = match.pre_match unless match.pre_match.empty?
        result << word
        s = match.post_match
      end
      # the last unmatched content, if any
      result << s
      result.compact!.map(&:strip!)
      
      pp result
      => ["force be", "you."]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-02
        • 1970-01-01
        • 2011-10-06
        • 1970-01-01
        • 1970-01-01
        • 2016-12-26
        • 2017-03-07
        • 2017-02-23
        相关资源
        最近更新 更多