2012-11-10 28 views
3

是否有任何方式使用RegEx.Matches來查找並回寫匹配值,但以不同(按字母順序)的順序?C#排序並放回Regex.matches

現在我有這樣的事情:

var pattern = @"(KEY `[\w]+?` \(`.*`*\))"; 
var keys = Regex.Matches(line, pattern); 

Console.WriteLine("\n\n"); 
foreach (Match match in keys) 
{ 
    Console.WriteLine(match.Index + " = " + match.Value.Replace("\n", "").Trim()); 
} 

但我真正需要的是採取table.sql轉儲和按字母順序排列現有的索引,例如代碼:

line = "...PRIMARY KEY (`communication_auto`),\n KEY `idx_current` (`current`),\n KEY `idx_communication` (`communication_id`,`current`),\n KEY `idx_volunteer` (`volunteer_id`,`current`),\n KEY `idx_template` (`template_id`,`current`)\n);" 

感謝 Ĵ


更新: 謝謝,m.buettner解決方案爲我提供了可用於繼續前進的基礎知識。我不是在正則表達式可悲的那麼好,但我結束了代碼,我認爲仍然可以提高:

... 
//sort INDEXES definitions alphabetically 
if (line.Contains(" KEY `")) line = Regex.Replace(
    line, 
    @"[ ]+(KEY `[\w]+` \([\w`,]+\),?\s*)+", 
    ReplaceCallbackLinq 
); 

static string ReplaceCallbackLinq(Match match) 
{ 
    var result = String.Join(",\n ", 
     from Capture item in match.Groups[1].Captures 
     orderby item.Value.Trim() 
     select item.Value.Trim().Replace("),", ")") 
    ); 
    return " " + result + "\n"; 
} 


更新: 還有一種情況,當索引字段是超過255個字符長的MySQL修剪指數高達255,這樣寫的:

KEY `idx3` (`app_property_definition_id`,`value`(255),`audit_current`), 

如此,爲了配合得這種情況下,我不得不改變一些代碼: 在ReplaceCall backLinq:

select item.Value.Trim().Replace("`),", "`)") 

和正則表達式定義:

@"[ ]+(KEY `[\w]+` \([\w`(\(255\)),]+\),?\s*)+", 

回答

2

這不能用正則表達式來完成。但是,您可以使用回調函數並利用.NET獨特的功能來捕獲同一個捕獲組中的多個事物。這樣你就可以避免使用Matches並自己寫回所有內容。反而你可以使用內置的Replace函數。我下面的例子只是對KEY短語進行排序,並將它們放回原樣(所以它只是在SQL語句中對它們進行排序)。如果您需要不同的輸出,您可以通過捕捉模式的不同部分並在最後調整操作來輕鬆實現。

首先,我們需要一個匹配評價傳遞迴調:

MatchEvaluator evaluator = new MatchEvaluator(ReplaceCallback); 

然後我們寫了整套一次指數相匹配,捕獲指數名稱的捕獲組正則表達式。我們把這種在Replace的重載需要一個評估:

output = Regex.Replace(
    input, 
    @"(KEY `([\w]+)` \(`[^`]*`(?:,`[^`]*`)*\),?\s*)+", 
    evaluator 
); 

現在,在大多數語言中,這是沒有用的,因爲由於重複捕獲組1將永遠只包含被捕獲的第一個或最後一件事(與捕獲組2相同)。但幸運的是,您正在使用C#,.NET的正則表達式引擎只是一個強大的野獸。因此,讓我們來看看回調函數,以及如何使用多個捕獲:

static string ReplaceCallback(Match match) 
{ 
    int captureCount = match.Groups[1].Captures.Count; 
    string[] indexNameArray = new string[captureCount]; 
    string[] keyBlockArray = new string[captureCount]; 
    for (int i = 0; i < captureCount; i++) 
    { 
     keyBlockArray[i] = match.Groups[1].Captures[i].Value; 
     indexNameArray[i] = match.Groups[2].Captures[i].Value; 
    } 
    Array.Sort(indexNameArray, keyBlockArray); 
    return String.Join("\n ", keyBlockArray); 
} 

match.Groups[i].Captures讓我們訪問一個組的多個捕獲。由於這些對象目前看起來並不真正有用,因此我們從它們的值中構建兩個字符串數組。然後我們使用Array.Sort,它根據一個值(這被認爲是關鍵字)的值對兩個數組進行排序。作爲「關鍵」我們使用表名的捕獲。由於「價值」我們使用完整的捕獲一個完整的塊。這將按照名稱排列完整的塊。然後,我們可以簡單地將塊連接在一起,添加之前使用的空白分隔符並將其返回。

+0

謝謝,這個解決方案給了我可以繼續使用的基礎知識。可悲的是,我並不擅長RegEx,但我最終得到的代碼相信還可以改進: –

0

不知道如果我完全理解這個問題,但沒有改變的foreach到:

foreach (Match match in keys.Cast<Match>().OrderBy(m => m.Value)) 

你想要什麼?

+0

謝謝,也許我還不夠清楚。你的代碼解決了一半的問題,後半部分是如何以這個新順序寫回 –