我正在尝试编写一个与一组字符匹配的正则表达式,而不考虑顺序。例如:
str = "act"
str.scan(/Insert expression here/)
会匹配:
cat
act
tca
atc
tac
cta
但不匹配ca
,ac
或cata
。
我在StackOverflow上阅读了很多类似的问题和答案,但没有找到与我的目标完全匹配的问题。
为了澄清一点,我正在使用ruby并且不想允许重复字符。
答案 0 :(得分:5)
这是您的解决方案
^(?:([act])(?!.*\1)){3}$
^ # matches the start of the string
(?: # open a non capturing group
([act]) # The characters that are allowed and a capturing group
(?!.*\1) # That character is matched only if it does not occur once more, Lookahead assertion
){3} # Defines the amount of characters
$
唯一特别的想法是lookahead assertion,以确保角色不会重复。
^
和$
是匹配字符串开头和结尾的锚点。
答案 1 :(得分:3)
[act]{3}
或^[act]{3}$
将在大多数正则表达式方言中执行此操作。如果您可以缩小您正在使用的系统范围,那么这将有助于您获得更具体的答案。
编辑:正如@georgydyer在下面的评论中提到的,从你的问题中不清楚是否允许重复的字符。如果没有,您可以调整this question的答案并获取:
^(?=[act]{3}$)(?!.*(.).*\1).*$
也就是说,检查匹配是一个积极的前瞻,然后是带有反向引用的负向前瞻以排除重复的字符。
答案 2 :(得分:2)
以下是我如何去做的事情:
regex = /\b(?:#{ Regexp.union(str.split('').permutation.map{ |a| a.join }).source })\b/
# => /(?:act|atc|cat|cta|tac|tca)/
%w[
cat act tca atc tac cta
ca ac cata
].each do |w|
puts '"%s" %s' % [w, w[regex] ? 'matches' : "doesn't match"]
end
输出:
"cat" matches
"act" matches
"tca" matches
"atc" matches
"tac" matches
"cta" matches
"ca" doesn't match
"ac" doesn't match
"cata" doesn't match
我使用将数组传递给Regexp.union
的技术来做很多事情;我使用散列的密钥工作得非常好,并将散列传递到gsub
以便在文本模板上进行快速搜索/替换。这是gsub
文档中的示例:
'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*') #=> "h3ll*"
Regexp.union创建了一个正则表达式,在提取生成的实际模式时,使用source
而不是to_s
非常重要:
puts regex.to_s
=> (?-mix:\b(?:act|atc|cat|cta|tac|tca)\b)
puts regex.source
=> \b(?:act|atc|cat|cta|tac|tca)\b
注意to_s
如何在模式字符串中嵌入模式的标志。如果您不期望它们,您可能会意外地将该模式嵌入到另一个模式中,而这种模式将无法按预期运行。去过那里,做了那件事,并将凹陷的头盔作为证据。
如果您真的想玩得开心,请查看CPAN上提供的Perl Regexp::Assemble模块。使用它,加上List::Permutor,让我们生成更复杂的模式。在像这样的简单字符串上,它不会节省太多空间,但是在长字符串或大型所需命中数组上,它可以产生巨大的差异。不幸的是,Ruby没有这样的东西,但是可以用单词或单词数组编写一个简单的Perl脚本,让它生成正则表达式并将其传回:
use List::Permutor;
use Regexp::Assemble;
my $regex_assembler = Regexp::Assemble->new;
my $perm = new List::Permutor split('', 'act');
while (my @set = $perm->next) {
$regex_assembler->add(join('', @set));
}
print $regex_assembler->re, "\n";
(?-xism:(?:a(?:ct|tc)|c(?:at|ta)|t(?:ac|ca)))
有关在Ruby中使用Regexp :: Assemble的更多信息,请参阅“Is there an efficient way to perform hundreds of text substitutions in Ruby?”。
答案 3 :(得分:1)
我将在这里假设几件事: - 您正在寻找给定角色的排列 - 你正在使用ruby
str = "act"
permutations = str.split(//).permutation.map{|p| p.join("")}
# and for the actual test
permutations.include?("cat")
但这不是正则表达式。
答案 4 :(得分:1)
毫无疑问 - 使用正面/负面前瞻和反向引用的正则表达式是光滑的,但是如果你只处理三个字符,我会通过明确枚举像@scones建议的字符排列而在冗长方面犯错误。
"act".split('').permutation.map(&:join)
=> ["act", "atc", "cat", "cta", "tac", "tca"]
如果您真的需要一个正则表达式来扫描更大的字符串,您可以随时:
Regexp.union "act".split('').permutation.map(&:join)
=> /\b(act|atc|cat|cta|tac|tca)\b/
显然,如果您的搜索字符串增长,此策略不会扩展,但在我看来,更容易观察到这样的代码的意图。
编辑:根据@ theTinMan的反馈,为cata
添加了误报的字边界。