2013-03-01 77 views
1

我正在尝试使用C#收集一些数据。我有一个以独特,非标准方式输出数据的系统。我需要定期从平面文件解析这些数据,并将其导入到数据库中。我也需要尽可能快地进行解析。数据库的东西我工作得很好,很简单。我需要帮助确定解析文件的最佳方式。目前,约有15000行,每天增加更多。这里看看数据。第一行是数据在平面文件中的呈现方式。第二个比较容易理解导入数据的视图。解析复杂的非标准字符串的最佳方法?

{a test entry} {{1}{{city}{chicago}{employee}{johnsmith}{building}{5}{room}{506A}{room}{506B}{id}{1234}}{2}{{city}{losangeles}{employee}{johnsmith}{building}{1}{room}{101A}{room}{102B}{id}{1234}}} 

{a test entry} 
{ 
    {1} 

    { 
     {city}  {chicago} 
     {employee} {johnsmith} 
     {building} {5} 
     {room}  {506A} 
     {room}  {506B} 
     {id}  {1234} 
    } 

    {2} 

    { 
     {city}  {losangeles} 
     {employee} {johnsmith} 
     {building} {1} 
     {room}  {101A} 
     {id}  {1234} 
    } 
} 

每个条目可以来自一个子条目(意味着{2}中没有数据)的任何地方,或者它可以继续有数百条。

我应该如何处理解析?我尝试了一些分裂和子串的东西,但我有不同的成功,而且速度很慢。

有什么办法可以简单地解析我正在看的数据?

+0

目前看起来相当直接。假设每个条目基本上都是具有相同基本属性的对象数组是否安全?格式是否有任何方法来识别字符串中的大括号 - 逃避某种形式? – Corey 2013-03-01 01:25:29

+0

线2的例子是什么? – Monso 2013-03-01 01:42:25

+0

除了它看起来像你可以有一个重复的键,在你的例子中的“房间”,它看起来与JSON非常相似,你可以很容易地熟练使用它。 (或作弊并使用<= v1.6 JSON,它允许重复的键)JSON会给你一个对象,你可以潜入或反序列化成一个对象,如果你知道结构,所以你可以创建一个类。 – Monso 2013-03-01 02:00:55

回答

3

通过字符创建堆栈和处理输入字符串的字符:

var stack = new Stack<StringBuilder>(); 
foreach (var ch in input) 
{ 
    if (ch == '{') 
    { 
     stack.Push(new StringBuilder()); 
    } 
    else if (ch == '}') 
    { 
     var item = stack.Pop().ToString(); 
     Console.WriteLine(new string(' ', stack.Count * 2) + item); 
    } 
    else if (stack.Count != 0) 
    { 
     stack.Peek().Append(ch); 
    } 
} 

输出:

a test entry 
    1 
    city 
    chicago 
    employee 
    johnsmith 
    building 
    5 
    room 
    506A 
    room 
    506B 
    id 
    1234 

    2 
    city 
    losangeles 
    employee 
    johnsmith 
    building 
    1 
    room 
    101A 
    room 
    102B 
    id 
    1234 

现在你已经分析的数据,你只需要弄清楚什么样的数据结构把它放进去。

+2

@PieterGeerkens:一个标记器会返回一个词汇符号流,比如'{''测试条目''} {''{''1''}''{'...我的代码做了一点更多的是,它实际上解析了输入。你是对的,它不会将数据反序列化为数据结构。但是,如果不知道OP需要什么样的数据结构,建议这个部分有点困难(并且,要求使用数据模型的完整解决方案,并且所有内容都有点太多以至于不能问imho)。 – dtb 2013-03-01 01:54:09

+0

我认为这会起作用。我可以调整它以适应我的需求。谢谢 – 2013-03-01 05:05:03

0

什么是这样的:

static void Main(string[] args) 
{ 
    int index = 0; 
    string text = "{a test entry} {{1}{{city}{chicago}{employee}{johnsmith}{building}{5}{room}{506A}{room}{506B}{id}{1234}}{2}{{city}{losangeles}{employee}{johnsmith}{building}{1}{room}{101A}{room}{102B}{id}{1234}}}"; 

    var tokens = Tokenize(text);   
    var node = Parse(new Node(new Token() { TokenType = TokenType.Root, Value = string.Empty }), tokens, ref index); 
    RaiseSubtrees(node); 

    Console.WriteLine(node.ToString()); 
} 

static List<Token> Tokenize(string text) 
{ 
    Stack<StringBuilder> stack = new Stack<StringBuilder>(); 
    List<Token> tokens = new List<Token>(); 

    foreach (var ch in text) 
    { 
     if (ch == '{') 
     { 
      stack.Push(new StringBuilder()); 
      tokens.Add(new Token(TokenType.ObjectStart, "{")); 
     } 
     else if (ch == '}') 
     { 
      var item = stack.Pop().ToString(); 

      if (!string.IsNullOrEmpty(item)) 
      { 
       tokens.Add(new Token(TokenType.Text, item)); 
      } 

      tokens.Add(new Token(TokenType.ObjectEnd, "}")); 
     } 
     else if (stack.Count != 0) 
     { 
      stack.Peek().Append(ch); 
     } 
    } 

    return tokens; 
} 

static Node Parse(Node parent, List<Token> tokens, ref int index) 
{ 
    for (; index < tokens.Count - 1; index++) 
    { 
     Token current = tokens[index]; 
     Token next = tokens[index + 1]; 

     if (current.TokenType == TokenType.ObjectStart) 
     { 
      Node child = new Node(current); 
      parent.Children.Add(child); 
      index++; 
      Parse(child, tokens, ref index); 
     } 
     else if (current.TokenType == TokenType.Entry || current.TokenType == TokenType.Text) 
     { 
      Node child = new Node(current); 
      parent.Children.Add(child); 
     } 
     else if (current.TokenType == TokenType.ObjectEnd) 
     { 
      return parent; 
     } 
    } 

    return parent; 
} 

static void RaiseSubtrees(Node node) 
{ 
    if (node.Children.Count == 1) 
    { 
     node.Token = node.Children.First().Token; 
     node.Children.Clear(); 
    } 
    else 
    { 
     foreach (Node child in node.Children) 
     { 
      RaiseSubtrees(child); 
     } 

     if (node.Children.All(c => c.Token.TokenType == TokenType.Text)) 
     { 
      for (int i = node.Children.Count - 1; i >= 1; i-=2) 
      { 
       Node keyNode = node.Children[i - 1]; 
       Node valueNode = node.Children[i]; 
       keyNode.Token.TokenType = TokenType.Key; 
       valueNode.Token.TokenType = TokenType.Value; 

       Node newParent = new Node(new Token(TokenType.Property, string.Empty)); 
       newParent.Children.Add(keyNode); 
       newParent.Children.Add(valueNode); 

       node.Children.RemoveAt(i); 
       node.Children.RemoveAt(i - 1); 
       node.Children.Insert(i - 1, newParent); 
      } 
     } 
    } 
} 

enum TokenType 
{ 
    Entry, 
    Key, 
    ObjectStart, 
    ObjectEnd, 
    Property, 
    Root, 
    Text, 
    Value 
} 

class Token 
{ 
    public TokenType TokenType { get; set; } 
    public string Value { get; set; } 

    public Token() 
    { 
    } 

    public Token(TokenType tokenType, string value) 
    { 
     this.TokenType = tokenType; 
     this.Value = value; 
    } 
} 

class Node 
{ 
    public Token Token { get; set; } 
    public IList<Node> Children { get; set; } 

    public Node(Token token) 
    { 
     this.Token = token; 
     this.Children = new List<Node>(); 
    } 

    public override string ToString() 
    { 
     StringBuilder builder = new StringBuilder(); 
     ToString(this, builder, string.Empty); 

     return builder.ToString(); 
    } 

    public void ToString(Node parent, StringBuilder builder, string indent) 
    { 
     builder.Append(indent).Append(parent.Token.TokenType.ToString()); 

     if (parent.Token.TokenType != TokenType.Root && parent.Token.TokenType != TokenType.ObjectStart) 
     { 
      builder.Append(": ").Append(parent.Token.Value); 
     } 

     builder.Append("\n"); 

     foreach (var child in parent.Children) 
     { 
      ToString(child, builder, indent + " "); 
     } 
    } 
} 

这将使用类似的方法来为DTB标记化,但后来我用Node类创建树模型的数据。这应该允许您以更结构化的方式处理数据。上面的Main方法的输出如下所示:

Root 
    Text: a test entry 
    ObjectStart 
    Text: 1 
    ObjectStart 
     Property: 
     Key: city 
     Value: chicago 
     Property: 
     Key: employee 
     Value: johnsmith 
     Property: 
     Key: building 
     Value: 5 
     Property: 
     Key: room 
     Value: 506A 
     Property: 
     Key: room 
     Value: 506B 
     Property: 
     Key: id 
     Value: 1234 
    Text: 2 
    ObjectStart 
     Property: 
     Key: city 
     Value: losangeles 
     Property: 
     Key: employee 
     Value: johnsmith 
     Property: 
     Key: building 
     Value: 1 
     Property: 
     Key: room 
     Value: 101A 
     Property: 
     Key: room 
     Value: 102B 
     Property: 
     Key: id 
     Value: 1234 
相关问题