2016-04-11 29 views
1

我有,我需要解析C#腳本,並尋找一定的方法屬性,並從中提取部分任務,我不知道是否有一個更優雅的方式比我如何做到這一點:什麼是更優雅的方式來解析這個字符串?

[Info("Title", "Author", "5.2.5", ResourceId = 819)]

這是我做的:

// foreach line in script 
if (line.Contains("[Info(") && line.Contains("ResourceId")) 
{ 
    var _attributes = line 
     .Replace(" ", "") 
     .Replace("\"", "") 
     .Replace("[Info(", "") 
     .Replace(")]", "") 
     .Replace("ResourceId=", "") 
     .Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); 
     // Do stuff with _attributes[0] _attributes[1] etc.. 
     break; 
} 
+1

文本解析最好用正則表達式完成。通過這個網站可以實現從字符串自動創建正則表達式:http://txt2re.com/ –

+0

這應該已經到了代碼審查也許? – Kixoka

+0

此任務非常適合正則表達式。但我不同意Siderite的看法,「測試解析最好用正則表達式來完成」。人們傾向於在不恰當的時候使用正則表達式。如果可能的話,總是嘗試使用比正則表達式更簡單的方法()(如字符串方法)。正則表達式很慢,並使用大量的內存。 – jdweng

回答

3

如果由於某些原因@Luaan建議無法完成,您可以使用這樣的表達式:\[Info\("(.+?)", "(.+?)", "([\d.]+)", ResourceId\s*=\s*(\d+)\)\]匹配並提取您的值在之後。

有一個示例here

編輯:正如@Evk指出的,這個表達式也會匹配註釋屬性。如果這不是你所追求的,請告訴我。

編輯:根據您的查詢,您需要使用類似如此的東西:\[Info\("(.+?)", "(.+?)", "?([\d.]+)"?, ResourceId\s*=\s*(\d+)\)\]。在這種情況下,第三個參數的引號後面跟着?字符,它指示引擎的引號可能不是。一個例子是here

+0

不要忘記以正確的方式處理註釋(//,/ * ... * /)。如果你只是使用這個正則表達式 - 它也會匹配所有的註釋屬性。 – Evk

+0

@Evk:是的,但是OP使用'.contains',並且評論代碼似乎不成問題。爲了以防萬一,我會添加一個註釋。 – npinti

+0

那麼這是更多的評論作者不要忘記這一點,因爲他可能沒有意識到他們可以評論。 – Evk

5

現在最簡單的解決方案是使用Roslyn。您可以解析代碼,找到的實際屬性(而不是看起來像您正在查找的屬性),並以C#-proper的方式處理它們。

一個簡單的例子:

var infoAttributes = CSharpSyntaxTree.ParseText(@" 
namespace MyNamespace 
{ 
    public class SomeClass 
    { 
    const string SomeConstant = ""Hi!""; 

    [Info(""Some book"", ""Ray Brandenburg"", ""5.2.5"", ResourceId = 819)] 
    public void SomeMethod() 
    { 

    } 

    [InfoAttribute(SomeConstant, 42, ""Banana"")] 
    pubic void SomeMethod2() 
    { 

    } 

    // [Info(""Not going to happen"", ""Hilary Clinton"", ""1.2.0"")] 
    public void SomeMethod3() 
    { 

    } 
    } 
} 
") 
.GetRoot() 
.DescendantNodes() 
.OfType<AttributeSyntax>() 
.Where(i => i.Name.ToString() == "Info" || i.Name.ToString() == "InfoAttribute") 
.Where 
(
    i => 
    i.ArgumentList.Arguments.Count(j => j.NameEquals == null) == 3 
    && i.ArgumentList.Arguments[0].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken) 
    && i.ArgumentList.Arguments[1].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken) 
    && i.ArgumentList.Arguments[2].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken) 
) 
.Select 
(
    i => 
    new 
    { 
    Title = (string)i.ArgumentList.Arguments[0].GetFirstToken().Value, 
    Author = (string)i.ArgumentList.Arguments[1].GetFirstToken().Value, 
    Version = (string)i.ArgumentList.Arguments[2].GetFirstToken().Value, 
    ResourceId = 
     i.ArgumentList.Arguments 
     .Where(j => j.NameEquals != null && j.NameEquals.Name.ToString() == "ResourceId") 
     .Select(j => j.ChildNodes().Skip(1).First().GetFirstToken().Value.ToString()) 
     .FirstOrDefault() 
    } 
); 

infoAttributes.Dump(); 

在這一水平上,這是僅執行源代碼的解析。爲了使事情變得更簡單,我添加了防禦性的子句,以使這些工作具有字面值 - 您可能希望將這些警告轉化爲手動或其他方式處理。代碼可以正確處理任何瑣事(例如空白),看起來像屬性聲明但不是的代碼,註釋和其他大量可能的問題。還有一個簡化的假設 - 值必須是文字(字符串或其他)。該示例只會找到一個Info屬性 - SomeMethod2上的一個使用常量和不同的構造函數過載,並且SomeMethod3上的一個被註釋掉。

另一個級別是從這個創建一個編譯樹。這涉及更多一點,但是可以讓所有工作都像真正的C#代碼一樣工作 - 例如,SomeMethod2上的屬性將正確解析SomeConstant。當然,如果你真的想100%正確,這需要收集所有的依賴關係等,這聽起來像是一種矯枉過正的行爲。除非在代碼中這是一個真正的問題,否則警告對於異常值應該很好。如果在代碼中經常使用局部常量,那麼擴展代碼以處理局部文字常量仍然非常容易。

作爲一個免責聲明,這肯定不是使用Roslyn進行解析的方式。這只是首先想到的,花了一段時間纔開始。我仍然在尋找更好的方法來處理羅斯林幾乎每天:)

+0

這確實很有趣,你能否提供一個如何獲得屬性和值的例子? –

+0

@ Dan-LeviTømta增加了示例代碼。請注意,根據您的具體要求,它可以或多或少地變得複雜 - 我採取了謹慎的方式,大部分都是簡單的假設。 – Luaan

+0

這真的很有趣,我不知道羅斯林,謝謝你讓我知道它,我會定義這個作爲後期項目的參考。 –

相關問題