2010-05-03 115 views
5

什麼是解析中的字符串解析在C#

"(params (abc 1.3)(sdc 2.0)(www 3.05)....)" 

形式的C#字符串轉換成結構形式

struct Params 
{ 
    double abc,sdc,www....; 
} 

感謝

編輯有效的方法 該結構始終具有相同的參數(相同的名稱,只有雙打,在編譯時已知)..但訂單沒有被授予..只有一個結構a一段時間..

+2

你能展示完整的例子來包含所有的樣本字符串嗎? – Glennular 2010-05-03 18:21:40

+0

很明顯你在問什麼。你想把a和b的值作爲什麼?結構是動態的,即它可能不總是相同的一組變量? – 2010-05-03 18:22:23

+1

不知道'abc'是多麼的雙倍,你可以多加一些嗎? – RedFilter 2010-05-03 18:22:34

回答

2

根據您的完整的語法,你有幾種選擇: 如果這是一個非常簡單的語法和你不必須以測試它的錯誤,你可以簡單地用下面去(這將是快)

var input = "(params (abc 1.3)(sdc 2.0)(www 3.05)....)"; 
var tokens = input.Split('('); 
var typeName = tokens[0]; 
//you'll need more than the type name (assembly/namespace) so I'll leave that to you 
Type t = getStructFromType(typeName); 
var obj = TypeDescriptor.CreateInstance(null, t, null, null); 
for(var i = 1;i<tokens.Length;i++) 
{ 
    var innerTokens = tokens[i].Trim(' ', ')').Split(' '); 
    var fieldName = innerTokens[0]; 
    var value = Convert.ToDouble(innerTokens[1]); 
    var field = t.GetField(fieldName); 
    field.SetValue(obj, value); 
} 

簡單的辦法但需要一個良好符合字符串或會出現異常。

如果語法稍微複雜一些,例如嵌套(),那麼這種簡單的方法將無法工作。

你可以嘗試使用regEx,但仍然需要一個相當簡單的語法,所以如果你最終有一個複雜的語法,你最好的選擇是一個真正的解析器。 Irony很容易使用,因爲你可以用簡單的c#編寫它(儘管有一些BNF的知識)。

2

你需要支持多個結構嗎?換句話說,這是否需要動態的;或者你知道編譯時的結構定義嗎?

用正則表達式解析字符串將是明顯的選擇。

這裏是一個正則表達式,將解析您的字符串格式:

private static readonly Regex regParser = new Regex(@"^\(params\s(\((?<name>[a-zA-Z]+)\s(?<value>[\d\.]+)\))+\)$", RegexOptions.Compiled); 

運行在一個字符串,正則表達式將會給你一個名爲「名」和「價值」兩組。每個組的Captures屬性將包含名稱和值。

如果結構類型在編譯時未知,那麼您將需要使用反射來填充字段。

如果您的意思是在運行時生成結構定義,則需要使用Reflection來發出類型;或者您將需要生成源代碼。

哪個部位有問題?

+1

如果性能至關重要,那麼RegEx不應該是第一選擇。它們的表現並不像簡單的字符串操作那樣簡單,例如Split和使用String.Split修剪 – 2010-05-03 19:45:33

0

是否要構建定義的語法的數據表示?

如果您正在尋找輕鬆的可維護性,無需編寫長的RegEx語句,您就可以構建自己的Lexer分析器。這裏是SO與答案聯繫好事先討論,以及幫助您

Poor man's "lexer" for C#

3
using System; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     struct Params 
     { 
      public double abc, sdc; 
     }; 

     static void Main(string[] args) 
     { 
      string s = "(params (abc 1.3)(sdc 2.0))"; 
      Params p = new Params(); 
      object pbox = (object)p; // structs must be boxed for SetValue() to work 

      string[] arr = s.Substring(8).Replace(")", "").Split(new char[] { ' ', '(', }, StringSplitOptions.RemoveEmptyEntries); 
      for (int i = 0; i < arr.Length; i+=2) 
       typeof(Params).GetField(arr[i]).SetValue(pbox, double.Parse(arr[i + 1])); 
      p = (Params)pbox; 
      Console.WriteLine("p.abc={0} p.sdc={1}", p.abc, p.sdc); 
     } 
    } 
} 

注意:如果您使用類而不是結構,則不需要裝箱/取消裝箱。

+0

+1。 – Brian 2010-05-03 19:24:30

+1

我想他想要一個動態構建的結構,可能通過一個Dictionary類型的對象。 (這個例子現在包括'www') – Glennular 2010-05-03 19:40:17

+0

@Glennular:他的編輯說結構是固定的。但無論如何我都同意你的意見。我寧願使用Dictionary 而不是像這樣的東西。 – 2010-05-03 20:11:08

2

一個正則表達式可以爲你做的工作:

public Dictionary<string, double> ParseString(string input){ 
    var dict = new Dictionary<string, double>(); 
    try 
    { 
     var re = new Regex(@"(?:\(params\s)?(?:\((?<n>[^\s]+)\s(?<v>[^\)]+)\))"); 
     foreach (Match m in re.Matches(input)) 
      dict.Add(m.Groups["n"].Value, double.Parse(m.Groups["v"].Value)); 
    } 
    catch 
    { 
     throw new Exception("Invalid format!"); 
    } 
    return dict; 
} 

這樣使用它:

string str = "(params (abc 1.3)(sdc 2.0)(www 3.05))"; 
var parsed = ParseString(str); 

// parsed["abc"] would now return 1.3 

可能適合比創造了很多不同的結構的每一個可能的輸入字符串,並使用更好反射填充它們。我不認爲這是值得的努力。

此外,我假設輸入字符串始終是您發佈的格式。

1

您可能會考慮執行足夠的字符串操作以使輸入看起來像標準命令行參數,然後使用像NDesk.Options這樣的現成命令行參數解析器來填充Params對象。你放棄了一些效率,但你在可維護性方面做得很好。

public Params Parse(string input) 
{ 
    var @params = new Params(); 
    var argv = ConvertToArgv(input); 
    new NDesk.Options.OptionSet 
     { 
      {"abc=", v => Double.TryParse(v, out @params.abc)}, 
      {"sdc=", v => Double.TryParse(v, out @params.sdc)}, 
      {"www=", v => Double.TryParse(v, out @params.www)} 
     } 
     .Parse(argv); 

    return @params; 
} 

private string[] ConvertToArgv(string input) 
{ 
    return input 
     .Replace('(', '-') 
     .Split(new[] {')', ' '}); 
} 
0

我只是做一個基本的遞歸下降分析器。它可能比你想要的更普遍,但沒有別的會更快。

0

下面是一個外的所述箱方法: 轉換()以{}和[SPACE]爲 「:」,然後使用System.Web.Script.Serialization.JavaScriptSerializer.Deserialize

string s = "(params (abc 1.3)(sdc 2.0))" 
    .Replace(" ", ":") 
    .Replace("(", "{") 
    .Replace(")","}"); 

return new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(s); 
+0

似乎對我來說,如果參數可以包含空格或者parens,那麼這將會打破所有地獄...... – cHao 2012-09-27 15:55:23