在Ruby中使用正则表达式将字符串拆分为单词列表

4
我想在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."]

更新:根据下面的评论,我修改了原来的脚本。我还创建了一个方法来拆分字符串。

 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."]

1
/(?:\bthe\b|\bwith\b)/ 最好写成 /\b(?:the|with)\b/ - the Tin Man
4个回答

4
我会用以下方法来实现:

我会这样做:

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

0

你必须对反斜杠进行转义:

"\\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 的问题。 - the Tin Man

0
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."
]

0
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."]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接