2010-02-23 3 views
7

¿Cómo puedo convertir este texto el contenido del archivo en una colección de objetos recursiva que pueda unirse a un TreeView? es decir, quiero terminar con una colección de objetos , el primero llamado países que tiene una colección de tres objetos secundarios: Francia, alemania, Italia, y así sucesivamente ...¿Cómo puedo convertir una lista de esquema de archivo de texto en una colección recursiva de objetos?

RESPUESTA: gracias a todos los que ayudaron en esto, aquí es mi código que analiza con éxito este esquema de texto en un árbol XAML: http://tanguay.info/web/index.php?pg=codeExamples&id=358

countries 
-france 
--paris 
--bordeaux 
-germany 
-italy 
subjects 
-math 
--algebra 
--calculus 
-science 
--chemistry 
--biology 
other 
-this 
-that 

El código debajo de está tan lejos como lo tengo, pero no trata correctamente con varios hijos de padres.

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace TestRecursive2342 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<OutlineObject> outlineObjects = new List<OutlineObject>(); 

      //convert file contents to object collection 
      List<string> lines = Helpers.GetFileAsLines(); 
      Stack<OutlineObject> stack = new Stack<OutlineObject>(); 
      foreach (var line in lines) 
      { 
       OutlineObject oo = new OutlineObject(line); 

       if (stack.Count > 0) 
       { 
        OutlineObject topObject = stack.Peek(); 
        if (topObject.Indent < oo.Indent) 
        { 
         topObject.OutlineObjects.Add(oo); 
         stack.Push(oo); 
        } 
        else 
        { 
         stack.Pop(); 
         stack.Push(oo);       
        } 

       } 
       else 
       { 
        stack.Push(oo); 
       } 

       if(oo.Indent == 0) 
        outlineObjects.Add(oo); 
      } 

      outlineObjects.ForEach(oo => Console.WriteLine(oo.Line)); 

      Console.ReadLine(); 
     } 
    } 

    public class OutlineObject 
    { 
     public List<OutlineObject> OutlineObjects { get; set; } 
     public string Line { get; set; } 
     public int Indent { get; set; } 

     public OutlineObject(string rawLine) 
     { 
      OutlineObjects = new List<OutlineObject>(); 
      Indent = rawLine.CountPrecedingDashes(); 
      Line = rawLine.Trim(new char[] { '-', ' ', '\t' }); 
     } 
    } 

    public static class Helpers 
    { 
     public static List<string> GetFileAsLines() 
     { 
      return new List<string> { 
       "countries", 
       "-france", 
       "--paris", 
       "--bordeaux", 
       "-germany", 
       "-italy", 
       "subjects", 
       "-math", 
       "--algebra", 
       "--calculus", 
       "-science", 
       "--chemistry", 
       "--biology", 
       "other", 
       "-this", 
       "-that"}; 
     } 

     public static int CountPrecedingDashes(this string line) 
     { 
      int tabs = 0; 
      StringBuilder sb = new StringBuilder(); 
      foreach (var c in line) 
      { 
       if (c == '-') 
        tabs++; 
       else 
        break; 
      } 
      return tabs; 
     } 
    } 
} 

Respuesta

2
public class Item 
{ 
    public string Name; 
    public Item Parent; 
} 

List<Item> Collection = new List<Item>(); 

public void Main() 
{ 
    var DataSource = data.InnerText; 

    StreamReader Reader = new StreamReader(MapPath("_test2.txt")); 
    int LastLevel = 0; 

    while (Reader.EndOfStream == false) { 
     var line = Reader.ReadLine(); 
     var Level = line.Where((System.Object c) => c == "-").Count; 
     Item LastItem = default(Item); 

     if (Collection.Count != 0) { 
      LastItem = Collection.Last(); 
     } 

     if (Level == 0) { 
      Collection.Add(new Item { Name = line }); 
      LastLevel = 0; 
     } 
     else if (Level - LastLevel == 1) { 
      Collection.Add(new Item { Name = line, Parent = LastItem }); 
      LastLevel += 1; 
     } 
     else if (Level == LastLevel) { 
      Collection.Add(new Item { Name = line, Parent = LastItem.Parent }); 
     } 
     else if (Level < LastLevel) { 
      var LevelDiff = LastLevel - Level; 
      Item Parent = LastItem; 

      for (i = 0; i <= LevelDiff; i++) { 
       Parent = Parent.Parent; 
      } 

      LastLevel = Level; 
      Collection.Add(new Item { Name = line, Parent = Parent }); 
     } 
    } 

    Reader.Close(); 
} 

Esto debería hacer el truco. Lo probé en tu archivo de texto. Puede haber algunos errores. Pruébalo y di si funciona.

EDITAR: En realidad después de más pruebas resulta que esto no funciona como se esperaba. Necesita agregar más lógica para que funcione. Te lo dejo a tí.

EDITAR: Después de probar el código un poco más, he llegado a una versión que funciona mejor. Todavía no puedo garantizar que funcione en todas las circunstancias.

+0

gracias, utilicé su patrón if/else para reestructurar mi código y ponerlo a funcionar: http://tanguay.info/web/index.php?pg=codeExamples&id=358 –

1

Usted debe hacer su OutlineObject contienen una lista de los niños OutlineObject s. De esta forma puede vincularse a la colección secundaria en vistas de árbol.

Mire here para ver un ejemplo. O here.


Para el análisis, se debe mantener un Stack<OutlineObject> de los objetos anidados. Cuando lea el siguiente OutlineObject, observe la profundidad del último OutlineObject en la pila. Si su nivel es mayor, se agrega como hijo de ese OutlineObject, y empuja su OutlineObject en la pila. Si su nivel es el mismo, elimine ese OutlineObject y empuje su objeto en su lugar. Si su nivel es más grande, quite la pila superior OutlineObject y repita el control.


En cuanto a su cambio para añadir

if (topObject.Indent < oo.Indent) 
{ 
    topObject.OutlineObjects.Add(oo); 
    stack.Push(oo); 
} 
else 
{ 
    stack.Pop(); 
    stack.Push(oo); 
} 

... este código no comprueba el caso cuando el nivel del nuevo objeto es menor que el nivel de la parte superior de la pila. Necesitarás:

... 
else if (topObject.Indent == oo.Indent) 
{ 
    stack.Pop(); 
    stack.Push(oo); 
} 
else 
{ 
    while (stack.Top().Indent >= oo.Indent) 
    stack.Pop(); 
    stack.Push(oo); 
} 
+0

sí OutlineObject tiene "Lista pública OutlineObjects {get; conjunto; } "y he creado un conjunto de OutlineObjects recursivos a mano y los he vinculado con éxito a un TreeView, pero lo que quiero hacer ahora es convertir una lista de texto en esa colección recursiva. –

+0

acaba de editar la publicación, respondiendo a su pregunta – Vlad

+0

great , eso me llevó mucho más lejos, publiqué mi código anterior, ahora tiene tres hijos raíz, pero las relaciones más profundas de los hijos/padres no son correctas por alguna razón, trabajará en ello, gracias. –

0

Simple.

Cree una lista de objetos OutlineObject, uno para cada nivel, estos servirán como los padres.

Por lo tanto, el algoritmo:

  1. crear el objeto de la línea
  2. Encuentre el nivel de sangrado (que será 0 para objetos raíz)
  3. Si la lista de los padres con menos de nivel +1 número de elementos, agregue elementos "nulos" hasta que tenga suficiente (lo que significa que para el primer objeto raíz, agregue un elemento "nulo" para que tenga 1 elemento)
  4. Reemplace el elemento #level en esa lista con el nuevo objeto que ha creado en 1. (dado que la lista está basada en 0, los objetos raíz serán los primeros)
  5. Si el nivel es> 0, agregarlo como un niño a los padres [nivel-1], si el nivel == 0, agregarlo como un objeto raíz

Esto debe darle su árbol estructura. Tendrá que mantener una lista de niños en cada objeto.

También tenga en cuenta que la lista anterior necesitará comprobación de errores adicional si lo desea controlar los errores en el archivo, así:

root 
-child 1 
--child 2 
another root 
--child 3 (note that we skipped a level) 

En este caso, el último hijo no se añadirá como un niño de "niño 1", no de "otra raíz".

+0

a la derecha, en mi código estoy haciendo los pasos 1 y 2, pero en el paso 3, ¿a qué te refieres con" lista de padres ", estoy manteniendo una lista de niños en cada objeto, quieres mantener seguimiento de una lista de padres también? –

+0

No, durante el proceso de lectura, simplemente mantiene una lista de los padres de cada nivel. Cada vez que encuentre un nuevo objeto raíz (nivel 0), reemplazará el objeto en esa lista. Cada vez que encuentre un nuevo objeto de nivel 1, usará el objeto en esa lista en el nivel 0 como su padre. –

0

El patrón Composite es la primera cosa que viene a mi mente ...

0

Aquí está mi intento que es una combinación de su esfuerzo original más el enfoque de diamandiev. También agregué un método recursivo de Salida() que efectivamente reproducirá el archivo de entrada original.

Desafortunadamente no pude entender el enfoque de la pila, pero me gustaría ver un ejemplo de trabajo.

Tenga en cuenta que esto solo permite su ejemplo dado de nodos anidados 3 niveles de profundidad. Más que eso requerirá una modificación en el cheque else if ((oo.Indent - lastItem.Indent) < 0).

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace TestRecursive2342 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<OutlineObject> outlineObjects = new List<OutlineObject>(); 

      //convert file contents to object collection 
      List<string> lines = Helpers.GetFileAsLines(); 

      OutlineObject lastItem = new OutlineObject(); 
      bool processOk = true; 

      foreach (var line in lines) 
      { 
       OutlineObject oo = new OutlineObject(line); 

       if (lastItem.Indent != -1) 
       { 
        if (oo.Indent == 0 && lastItem.Indent != 0) 
        { 
         // we've got a new root node item, so add the last item's top level parent to the list 
         while (lastItem.parent != null) 
          lastItem = lastItem.parent; 

         outlineObjects.Add(lastItem); 
        } 
        else if ((oo.Indent - lastItem.Indent) == 1) 
        { 
         // new item is one level lower than the last item 
         oo.parent = lastItem; 
         lastItem.OutlineObjects.Add(oo); 
        } 
        else if (oo.Indent == lastItem.Indent) 
        { 
         // new item is at the same level as the last item 
         oo.parent = lastItem.parent; 
         lastItem.parent.OutlineObjects.Add(oo); 
        } 
        else if ((oo.Indent - lastItem.Indent) < 0) 
        { 
         // new item is above the last item, but not a root node 
         // NB: this only allows for an item to be two levels above the last item 
         oo.parent = lastItem.parent.parent; 
         lastItem.parent.parent.OutlineObjects.Add(oo); 
        } 
        else if ((oo.Indent - lastItem.Indent) > 1) 
        { 
         // missing node check 
         Console.WriteLine("ERROR: missing node in input file between \"{0}\" and \"{1}\"", lastItem.Line, oo.Line); 
         processOk = false; 
         break; 
        } 
       } 

       lastItem = oo; 
      } 

      if (processOk) 
      { 
       // flush the last item 
       while (lastItem.parent != null) 
        lastItem = lastItem.parent; 

       outlineObjects.Add(lastItem); 

       outlineObjects.ForEach(oo => oo.Output()); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public class OutlineObject 
    { 
     public OutlineObject parent { get; set; } 
     public List<OutlineObject> OutlineObjects { get; set; } 

     public string Line { get; set; } 
     public int Indent { get; set; } 

     public void Output() 
     { 
      StringBuilder sb = new StringBuilder(); 
      sb.Append('-', this.Indent); 
      sb.Append(this.Line); 

      Console.WriteLine(sb); 

      foreach (OutlineObject oChild in this.OutlineObjects) 
      { 
       oChild.Output(); 
      } 
     } 

     public OutlineObject() 
     { 
      parent = null; 
      OutlineObjects = new List<OutlineObject>(); 
      Line = ""; 
      Indent = -1; 
     } 

     public OutlineObject(string rawLine) 
     { 
      OutlineObjects = new List<OutlineObject>(); 
      Indent = rawLine.CountPrecedingDashes(); 
      Line = rawLine.Trim(new char[] { '-', ' ', '\t' }); 
     } 
    } 

    public static class Helpers 
    { 
     public static List<string> GetFileAsLines() 
     { 
      return new List<string> { 
       "countries", 
       "-france", 
       "--paris", 
       "--bordeaux", 
       "-germany", 
       "-italy", 
       "subjects", 
       "-math", 
       "--algebra", 
       "--calculus", 
       "-science", 
       "--chemistry", 
       "--biology", 
       "other", 
       "-this", 
       "-that"}; 
     } 

     public static int CountPrecedingDashes(this string line) 
     { 
      int tabs = 0; 

      foreach (var c in line) 
      { 
       if (c == '-') 
        tabs++; 
       else 
        break; 
      } 
      return tabs; 
     } 
    } 
} 
0

¡Qué gran solución! Esto podría ser una pequeña utilidad práctica. Es perfecto.

Sé que ha pasado un tiempo desde que publicaste esto; No pude localizar el original pero encontré una copia archivada here.

Lo he modificado un poco por brevedad y lo traduje a VB.NET para aquellos que puedan estar interesados.

Aquí está el resultado final:

principal

Module Main 
    Sub Main() 

    With New Test 
     .Render() 
    End With 

    Console.WriteLine() 
    Console.Write("Press any key to exit...") 
    Console.ReadKey() 
    End Sub 
End Module 

prueba

Public Class Test 
    Private ReadOnly Tree As Tree 

    Public Sub New() 
    Me.Tree = New Tree(Me.Text, "-", 1) 
    End Sub 



    Public Sub Render() 
    Me.Render(Me.Tree.Nodes) 
    End Sub 



    Public Sub Render(Nodes As List(Of Node)) 
    Nodes.ForEach(Sub(Node As Node) 
        Console.WriteLine("{0}{1}", Space(Node.Level), Node.Text) 

        Me.Render(Node.Nodes) 
        End Sub) 
    End Sub 



    Private ReadOnly Property Text As String 
    Get 
     Return _ 
     "TEST DATA" & vbCrLf & 
     "countries" & vbCrLf & 
     "-france" & vbCrLf & 
     "--paris" & vbCrLf & 
     "--bordeaux" & vbCrLf & 
     "-germany" & vbCrLf & 
     "--hamburg" & vbCrLf & 
     "--berlin" & vbCrLf & 
     "--hannover" & vbCrLf & 
     "--munich" & vbCrLf & 
     "-italy" & vbCrLf & 
     "subjects" & vbCrLf & 
     "-math" & vbCrLf & 
     "--algebra" & vbCrLf & 
     "--calculus" & vbCrLf & 
     "-science" & vbCrLf & 
     "--chemistry" & vbCrLf & 
     "--biology" & vbCrLf & 
     "other" & vbCrLf & 
     "-this" & vbCrLf & 
     "-that" 
    End Get 
    End Property 
End Class 

árbol

Public Class Tree 
    Private Level As Integer 

    Public Sub New(Text As String, LevelIndicator As String) 
    Me.New(Text, LevelIndicator, 0) 
    End Sub 



    Public Sub New(Text As String, LevelIndicator As String, StartingIndex As Integer) 
    Me.Load(Text, LevelIndicator, StartingIndex) 
    End Sub 



    Public ReadOnly Property Nodes As List(Of Node) 
    Get 
     Return _Nodes 
    End Get 
    End Property 
    Private ReadOnly _Nodes As New List(Of Node) 



    Private Sub Load(Text As String, LevelIndicator As String, StartingIndex As Integer) 
    Dim iLevel As Integer 
    Dim oParents As Stack(Of Node) 
    Dim oNode As Node 

    oParents = New Stack(Of Node) 

    Text.ToLines(StartingIndex).ForEach(Sub(Line As String) 
              oNode = New Node(Line, LevelIndicator) 

              If oNode.Level = 0 Then ' Root ' 
              Me.Nodes.Add(oNode) 
              oParents.Push(oNode) 
              Me.Level = 0 

              ElseIf oNode.Level - Me.Level > 1 Then ' Skipped generation(s) ' 
              Throw New FormatException("The outline structure is invalid.") 

              ElseIf oNode.Level = Me.Level Then ' Sibling ' 
              oParents.Pop() 
              Me.Level = oParents.SetNode(oNode, Me.Level) 

              ElseIf oNode.Level - Me.Level = 1 Then ' Child ' 
              Me.Level = oParents.SetNode(oNode, Me.Level + 1) 

              ElseIf oNode.Level < Me.Level Then ' Walk back up the stack ' 
              For iLevel = 0 To Me.Level - oNode.Level 
               oParents.Pop() 
              Next 

              Me.Level = oParents.SetNode(oNode, oNode.Level) 

              End If 
             End Sub) 
    End Sub 
End Class 

Nodo

Public Class Node 
    Public Sub New(Line As String, LevelIndicator As String) 
    _Level = Line.PrefixCount(LevelIndicator) 
    _Text = Line.StripPrefix(LevelIndicator) 
    End Sub 



    Public ReadOnly Property Nodes As List(Of Node) 
    Get 
     Return _Nodes 
    End Get 
    End Property 
    Private ReadOnly _Nodes As New List(Of Node) 



    Public ReadOnly Property Level As Integer 
    Get 
     Return _Level 
    End Get 
    End Property 
    Private ReadOnly _Level As Integer 



    Public ReadOnly Property Text As String 
    Get 
     Return _Text 
    End Get 
    End Property 
    Private ReadOnly _Text As String 
End Class 

Extensiones

Public Module Extensions 
    <Extension> 
    Public Function PrefixCount(Text As String, Prefix As String) As Integer 
    Dim iIndex As Integer 

    PrefixCount = 0 

    Do While Text.StartsWith(Prefix) 
     iIndex = Text.IndexOf(Prefix) 

     If iIndex = -1 Then 
     Exit Do 
     Else 
     Text = Text.Substring(iIndex + Prefix.Length) 
     PrefixCount += 1 
     End If 
    Loop 
    End Function 



    <Extension> 
    Public Function StripPrefix(Text As String, Prefix As String) As String 
    StripPrefix = Text 

    Do While StripPrefix.StartsWith(Prefix) 
     StripPrefix = StripPrefix.Substring(Prefix.Length) 
    Loop 
    End Function 



    <Extension> 
    Public Function ToLines(Text As String, StartingIndex As Integer) As List(Of String) 
    Return Split(Text, vbCrLf).Where(Function(Line As String) 
             Return Line.IsNullOrWhiteSpace = False 
            End Function).Skip(StartingIndex).ToList 
    End Function 



    <Extension> 
    Public Function SetNode(Parents As Stack(Of Node), Node As Node, Level As Integer) As Integer 
    Parents.Peek.Nodes.Add(Node) 
    Parents.Push(Node) 

    Return Level 
    End Function 



    <Extension> 
    Public Function ToFormat(Template As String, ParamArray Values As Object()) As String 
    Return String.Format(Template, Values) 
    End Function 
End Module 
Cuestiones relacionadas