[英]Checking collision in filename search patterns with wildcards
我需要通過僅檢查/比較表達式來比較文件系統通配符表達式以查看它們的結果是否重疊。
為了便於舉例,我們正在構建一個實用程序,它可以根據文件系統通配符表達式將文件從一個(或多個位置)排序到單獨的文件夾中。 例如:* .txt進入文件夾a,* .doc進入文件夾b,依此類推。 我們支持的通配符是*和?
我希望能夠通過分析通配符表達式確定它們是否會沖突/重疊。
例如,如果我有以下表達式:
*.x.y *.y
它們會沖突(重疊),因為第二個表達式* .y將包含* .xy結果。 (例如Axy會匹配兩個表達式)
我正在通過使用所有表達式構建樹結構來接近這一點,認為如果表達式沖突,構建樹的行為將失敗。
For example: *.x a.b a.c b.d might create a tree like +-*-.-x | start +--+ | +-b | | +-a-.-+-c | | +-b-.-d
如果我嘗試添加模式bx,則樹將在* .x路徑后成功,從而表示模式已存在。
我正朝着正確的方向前進嗎? 或者是否有一種已知的攻擊方法?
要檢查兩個通配符模式是否可以匹配相同的文件名,您可以將此問題視為創建字符對之間的比較網格,然后檢查是否存在對角線路徑。 下圖顯示了通配符模式ab?.c??
和a*bc.*
可以檢查可能的沖突:
當找到兩個相同文字字符之間的匹配時,您將對角移動到下一個要檢查的字符。
當一個文字字符和一個單字符的外卡?
遇到這種情況,有兩種可能性:通配符匹配字符(對角移動),或通配符匹配空白空間,然后跳過它。
當遇到多字符外卡*
時,需要考慮三種可能性:外卡與另一個字符前的空格匹配,外卡與另一個字符匹配,或外卡匹配多個字符。
代碼示例1(迭代)
下面是一個簡單的javascript實現,它在對角線上迭代網格,標記可以從當前單元格到達的單元格,然后檢查右下角單元格是否可達。 運行代碼段以查看一些示例。
function wildConflict(wild1, wild2) { var grid = [[true]], width = wild1.length, height = wild2.length; for (var x = 1; x <= width; x++) grid[x] = []; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { if (grid[x][y]) { var a = wild1.charAt(x), b = wild2.charAt(y); if (a == '*' || b == '*' || a == '?' || b == '?' || a == b) grid[x + 1][y + 1] = true; if (a == '*' || b == '*' || a == '?') grid[x + 1][y] = true; if (a == '*' || b == '*' || b == '?') grid[x][y + 1] = true; } } } return grid[width][height] == true; } var a = ["a", "a", "a*", "abc", "a*", "*.xy", "*.xy", "a*b*", "a*bc.*", "a?c.de"]; var b = ["a", "b", "abc", "a?", "*b", "*.y", "*.x", "a*c*", "ab?.c??", "ac.d??"]; for (var i in a) document.write(""" + a[i] + "" ↔ "" + b[i] + "" → " + wildConflict(a[i], b[i]) + "<BR>");
代碼示例2(遞歸)
簡單的遞歸實現具有可能多次檢查一些字符對的缺點。 它不需要2D數組,但遞歸顯然也使用了內存。
請注意,當遇到多字符通配符*
時,算法僅使用兩種可能性進行遞歸:跳過一個字符,或跳過另一個字符; 當將通配符與下一個字符進行比較時,在下一步中將跳過兩個字符(即,外卡匹配一個字符)。
function wildConflict(wild1, wild2) { var w1 = wild1.split(''), w2 = wild2.split(''); return conflict(0, 0); function conflict(p1, p2) { if (p1 == w1.length || p2 == w2.length) { if ((p1 == w1.length && p2 == w2.length) || (p1 == w1.length - 1 && (w1[p1] == '*' || w1[p1] == '?')) || (p2 == w2.length - 1 && (w2[p2] == '*' || w2[p2] == '?'))) { return true; } else return false; // premature end } else if (w1[p1] == '*' || w2[p2] == '*' || (w1[p1] == '?' && w2[p2] == '?')) { return conflict(p1 + 1, p2) || conflict(p1, p2 + 1); } else if (w1[p1] == '?') { return conflict(p1 + 1, p2) || conflict(p1 + 1, p2 + 1); } else if (w2[p2] == '?') { return conflict(p1, p2 + 1) || conflict(p1 + 1, p2 + 1); } else if (w1[p1] == w2[p2]) { return conflict(p1 + 1, p2 + 1); } else return false; // unequal literals } } var x = ["a", "a", "a*", "abc", "a*", "*.xy", "*.xy", "a*b*", "a*bc.*", "a?c.de"]; var y = ["a", "b", "abc", "a?", "*b", "*.y", "*.x", "a*c*", "ab?.c??", "ac.d??"]; for (var i in x) document.write(""" + x[i] + "" ↔ "" + y[i] + "" → " + wildConflict(x[i], y[i]) + "<BR>");
將每個通配符表達式轉換為與其匹配的有限自動機。
計算有限自動機的交集。
使用動態編程來查看交叉點是否匹配。
如果您不認識這些概念,請參閱幾年前嘗試解釋它的數字排除算法 。 (此時用於計算與正則表達式集合匹配的內容,但原則相同。)
我想你可以把模式變成正則表達式,然后看看它們是否相互匹配? 此解決方案是基於對MSDN Directory.GetFiles規則 -我覺得有一些錯誤,但我不知道是什么。
這是一個基本的實現
private bool Equivalent(string patternOne, string patternTwo)
{
// convert both patterns to regexes based on rules for Directory.GetFiles
var expressionOne = FilePatternToRegex(patternOne);
var expressionTwo = FilePatternToRegex(patternTwo);
// if either regex matches the opposite pattern, we've got a conflict
return expressionTwo.IsMatch(patternOne) || expressionOne.IsMatch(patternTwo);
}
Regex FilePatternToRegex(string pattern)
{
// separate extension and filename
var extension = Path.GetExtension(pattern);
var filename = Path.GetFileNameWithoutExtension(pattern);
// escape filename
filename = EscapeFilePattern(filename);
// 3 character extensions are a special case -- should be greedy eg xls matches xlsx
// extension.Length == 4 bc its dot AND 3 characters
if (extension.Length == 4 && !extension.Contains("*") && !extension.Contains("?"))
{
extension = extension + ".*";
}
else
{
// all other extension lengths just get escaped like normal regexes
extension = EscapeFilePattern(extension);
}
// our final pattern should also only match at the string start/end
var finalPattern = "\\A" + filename + extension + "\\z";
return new Regex(finalPattern);
}
string EscapeFilePattern(string pattern)
{
// escape star and question mark bc they are filepattern significant
pattern = pattern.Replace("*", "%S%").Replace("?", "%Q%");
// escape all other special regex characters
pattern = Regex.Escape(pattern);
// turn star and question mark into their regex equivalents
pattern = pattern.Replace("%S%", ".+").Replace("%Q%", ".");
return pattern;
}
編輯 :根據評論中的進一步討論,這是打破。 使用代碼示例證明它已被破壞:
var dir = new DirectoryInfo(Environment.CurrentDirectory).CreateSubdirectory(Guid.NewGuid().ToString());
var path = Path.Combine(dir.FullName, "abc");
File.WriteAllText(path, "*");
// verify both patterns match our file
Assert.AreEqual(path, dir.GetFiles("a*c*")[0].FullName);
Assert.AreEqual(path, dir.GetFiles("a*b*")[0].FullName);
// current regex based solution thinks they are NOT equivalent
// when they are
Assert.IsFalse(Equivalent("a*c*", "a*b*"));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.