如何编写与任何顺序的字符匹配的正则表达式?

我正在尝试编写一个正则表达式,它将匹配一组字符而不考虑顺序。 例如:

str = "act" str.scan(/Insert expression here/) 

会匹配:

 cat act tca atc tac cta 

但不会匹配caaccata

我在StackOverflow上阅读了很多类似的问题和答案,但没有找到一个与我的目标完全匹配的问题。

为了澄清一点,我使用ruby并且不想允许重复字符。

这是你的解决方案

 ^(?:([act])(?!.*\1)){3}$ 

在Regexr上看到它

 ^ # 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 $ 

唯一特别的想法是前瞻性断言 ,以确保不再重复这个角色。

^$是匹配字符串开头和结尾的锚点。

[act]{3}^[act]{3}$将在大多数正则表达式方言中执行此操作。 如果您可以缩小您正在使用的系统范围,这将有助于您获得更具体的答案。

编辑:正如@georgydyer在下面的评论中所提到的,从你的问题中不清楚是否允许重复的字符。 如果没有,你可以调整这个问题的答案,得到:

 ^(?=[act]{3}$)(?!.*(.).*\1).*$ 

也就是说,检查匹配是一个积极的先行,然后是带有反向引用的负向前瞻以排除重复的字符。

这是我如何去做的:

 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的技术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))) 

有关使用Regexp :: Assemble with Ruby的更多信息,请参阅“ 有没有一种有效的方法在Ruby中执行数百个文本替换? ”。

我将在这里假设几件事: – 你正在寻找给定角色的排列 – 你正在使用ruby

 str = "act" permutations = str.split(//).permutation.map{|p| p.join("")} # and for the actual test permutations.include?("cat") 

这不是正则表达式。

毫无疑问 – 使用正/负前瞻和反向引用的正则表达式是光滑的,但如果你只处理三个字符,我会通过明确枚举像@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添加了误报的单词边界。