我需要检查一个长文本字符串中是否存在大型(60,000+元素)数组的任何元素。我目前的代码如下:
if $TARGET_PARTLIST.any? { |target_pn| pdf_content_string.include? target_pn }
self.last_match_code = target_pn
self.is_a_match = true
end
我收到语法错误undefined local variable or method target_pn
。
有人能让我知道这段代码使用的正确语法吗?此外,如果有人知道更快的方法,我全都耳朵!
答案 0 :(得分:3)
在这种情况下,您的所有语法都是正确的,您只是遇到了逻辑错误。虽然target_pn
在传递给any?
的块内被定义(作为参数),但它没有在if
语句的块中定义,因为any?
的范围 - 块以结束大括号结束,而target_pn在其范围之外不可用。您的代码的正确(和更惯用)版本将如下所示:
self.is_a_match = $TARGET_PARTLIST.any? do |target_pn|
included = pdf_content_string.include? target_pn
self.last_match_code = target_pn if included
included
end
或者,正如jvillian所说的那样,可以将字符串转换为单词数组,然后进行交集并查看结果集是否为非空。像这样:
self.is_a_match = !($TARGET_PARTLIST &
pdf_content_string.gsub(/[^A-Za-z ]/,"")
.split).empty?
不幸的是,这种方法失去了self.last_match_code
。作为塞尔吉奥指出的一个注释,如果您正在处理非英语语言,则必须更改上述正则表达式。
希望有所帮助!
答案 1 :(得分:2)
您应该使用Enumerable#find而不是Enumerable#any?。
found = $TARGET_PARTLIST.find { |target_pn| pdf_content_string.include? target_pn }
if found
self.last_match_code = found
self.is_a_match = true
end
请注意,这并不能确保字符串包含一个$TARGET_PARTLIST
元素的单词。例如,如果$TARGET_PARTLIST
包含单词" able",则该字符串将在字符串中找到,"您觉得舒服吗?"。如果您只想匹配单词,则可以执行以下操作。
found = $TARGET_PARTLIST.find { |target_pn| pdf_content_string[/\b#{target_pn}\b/] }
请注意,这使用方法String#[]。
\b
是正则表达式中的单词中断,这意味着匹配的第一个(最后一个)字符不能在单词字符(字母,数字)之前(后跟)出现或者下划线)。
如果速度很重要,使用以下内容可能会更快。
found = $TARGET_PARTLIST.find { |target_pn|
pdf_content_string.include?(target_on) && pdf_content_string[/\b#{target_pn}\b/] }
答案 2 :(得分:0)
一种可能更高效的方法是让Regexp搜索它,将所有这些转移到本机代码中。
# needed only once
TARGET_PARTLIST_RE = Regexp.new("\\b(?:#{$TARGET_PARTLIST.sort.map { |pl| Regexp.escape(pl) }.join('|')})\\b")
# to check
self.last_match_code = pdf_content_string[TARGET_PARTLIST_RE]
self.is_a_match = !self.last_match_code.nil?
更高效的方法是构建前缀树并使用前缀树创建正则表达式(这会优化正则表达式查找),但这需要更多工作:)