C#中的字符串解析

什么是以#的forms解析C#字符串的有效方法

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

到表单中的结构

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

谢谢

编辑结构总是有相同的参数(相同的名称,只有双打,在编译时已知)..但订单不被授予..一次只有一个结构..

根据你的完整语法,你有几个选择:如果它是一个非常简单的语法,你不必测试它中的错误,你可以简单地使用下面的(这将是快速的)

 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 

然而,这种简单的方法需要一个符合要求的字符串,否则会产生不良行为。

如果语法有点复杂,例如嵌套(),那么这种简单的方法将无效。

你可以尝试使用regEx,但仍然需要一个相当简单的语法,所以如果你最终有一个复杂的语法,你最好的选择是一个真正的解析器。 反讽很容易使用,因为你可以用简单的c#来写它(虽然有些BNF的知识是加分的)。

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

注意:如果您使用的是类而不是结构,则不需要装箱/拆箱。

你需要支持多个结构吗? 换句话说,这需要是动态的吗? 或者您是否在编译时知道结构定义?

用正则表达式解析字符串将是显而易见的选择。

这是一个正则表达式,它将解析您的字符串格式:

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

在字符串上运行该正则表达式将为您提供两个名为“name”和“value”的组。 每个组的Captures属性将包含名称和值。

如果在编译时结构类型未知,那么您将需要使用reflection来填充字段。

如果您想在运行时生成结构定义,则需要使用Reflection来发出类型; 或者你需要生成源代码。

你遇到哪个问题?

正则表达式可以为您完成这项工作:

 public Dictionary ParseString(string input){ var dict = new Dictionary(); try { var re = new Regex(@"(?:\(params\s)?(?:\((?[^\s]+)\s(?[^\)]+)\))"); 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 

这可能比为每个可能的输入字符串创建许多不同的结构,并使用reflection来填充它们更合适。 我不认为这是值得的。

此外,我假设输入字符串始终与您发布的格式完全相同。

您可能会考虑执行足够的字符串操作以使输入看起来像标准命令行参数,然后使用现成的命令行参数解析器(如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[] {')', ' '}); } 

您想构建定义语法的数据表示吗?

如果您正在寻找易于维护,无需编写长RegEx语句,您可以构建自己的Lexer解析器。 这里是关于SO的事先讨论,在答案中有很好的联系,也可以帮助你

穷人为C#“lexer”

我只会做一个基本的递归下降解析器。 它可能比你想要的更普遍,但没有别的东西会更快。

这是一个开箱即用的方法:convert()到{}和[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);