可能会遇到以下两行文字:
约翰的新车
约翰的车
修饰符“new”是可选的。我认为这样可行:
([a-zA-Z'\s]+)\s?(new)?\s?(car)
根据Rubular的说法,对于第一种情况,这给出了["John's new", "", "car"]
。我正在寻找的是:
约翰的新车
["John's", "new", "car"]
在这种情况下:
John's car
["John's", {}, "car"]
答案 0 :(得分:2)
([a-zA-Z'\s]+?)\s?(new)?\s?(car)
^ added
你需要让第一个子组不贪婪,它正在吃掉第二个子组匹配。
>> /([a-zA-Z'\s]+?)\s?(new)?\s?(car)/.match "John's new car"
=> #<MatchData "John's new car" 1:"John's" 2:"new" 3:"car">
>> /([a-zA-Z'\s]+?)\s?(new)?\s?(car)/.match "John's car"
=> #<MatchData "John's car" 1:"John's" 2:nil 3:"car">
答案 1 :(得分:2)
答案 2 :(得分:1)
是的,可能遇到其他的话。但我会用([a-zA-Z'\ s] +)\ s?(新|旧|快|慢)?\ s?(汽车)
这不是一个好的计划,因为你可以有一个非常大的可选单词列表和一个更新源代码的持续任务。
更好的解决方案是将可选单词放入YAML文件,在运行时加载,从中创建正则表达式,并将其插入到正确位置的模式中。
为什么选择YAML格式?它易于阅读,并且可以通过多种语言轻松加载/解析。如果选择,可以使用文本平面文件。
为什么要创建正则表达式而不是循环遍历列表?因为如果正确完成,正则表达式会更快更准确。
以下是我如何去做的事情:
将其保存到名为“test.yaml”的YAML文件中:
---
- red
- blue
- green
- yellow
- fast
- slow
- old
- new
将其保存为'test.rb':
这就是我捕鱼的方式:
#!/usr/bin/env ruby
require 'pp'
require 'yaml'
adjectives = YAML.load_file('./test.yaml')
adjective_regex = /(?:\b#{ Regexp.union(adjectives).source }\b)/i
search_regex = /([a-z']+) \s+ (#{ adjective_regex }?) \s? (car)/ix
[
"John's car",
*adjectives.map{ |a| "John's #{ a } car" }
].each do |s|
s[search_regex]
pp [$1, $2.empty? ? {} : $2, $3]
end
运行输出:
["John's", {}, "car"]
["John's", "red", "car"]
["John's", "blue", "car"]
["John's", "green", "car"]
["John's", "yellow", "car"]
["John's", "fast", "car"]
["John's", "slow", "car"]
["John's", "old", "car"]
["John's", "new", "car"]
此时,维护应用程序不需要修改代码,而是修改数据。
现在,Perl有一个名为Regexp::Assemble的模块,对于这种用途非常有用。它允许我们获取单词列表并生成一个非常有效的模式来处理搜索:
而不是像"red|blue|green|yellow|fast|slow|old|new"
那样的正则表达式,它看起来像:
(?-xism:(?:(?:(?:yel|s)lo|ne)w|(?:ol|re)d|green|blue|fast))
以下是生成该模式的代码:
use Regexp::Assemble;
my $ra = Regexp::Assemble->new;
my @adjectives = qw[red blue green yellow fast slow old new];
foreach my $a (@adjectives) {
$ra->add($a);
}
print $ra->re, "\n";
此示例不会创建更短的模式,但添加的单词越多,模式获得的优化程度就越高。它能产生的效果非常惊人。重要的是你可以轻松地使用代码生成列表,并使用它为Ruby的解析器构建一个正则表达式。