簡體   English   中英

如何編寫與任何順序的字符匹配的正則表達式?

[英]How do I write a regular expression that will match characters in any order?

我正在嘗試編寫一個正則表達式,它將匹配一組字符而不考慮順序。 例如:

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)))

請參閱“ 有沒有一種有效的方法在Ruby中執行數百個文本替換? ”,以獲取有關在Ruby中使用Regexp :: Assemble的更多信息。

我將在這里假設幾件事: - 你正在尋找給定角色的排列 - 你正在使用紅寶石

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添加了誤報的單詞邊界。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM