2010-02-23 9 views
7

Comment puis-je convertir ce fichier texte contenu dans une collection d'objets récursif que je peux lier à un TreeView? à-dire que je veux finir avec une collection de 3 objets, le premier appelé pays qui a une collection de trois objets enfants: France, Allemagne, italie, et ainsi de suite ...Comment puis-je convertir une liste de plan de fichier texte en une collection récursive d'objets?

RÉPONSE: grâce à tous ceux qui ont aidé à ce sujet, voici mon code qui analyse avec succès ce plan de texte dans un arbre 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 

Le code ci-dessous est aussi loin que je l'ai eu, mais il ne traite pas correctement avec plusieurs enfants de parents.

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

Répondre

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

Cela devrait faire l'affaire. Je l'ai testé sur votre fichier texte. Il pourrait y avoir des bugs. Testez-le et dites si cela fonctionne.

EDIT: En fait, après d'autres tests, il s'avère que cela ne fonctionne pas comme prévu. Vous devez ajouter plus de logique pour le faire fonctionner. Je vous laisse ça.

EDIT: Après avoir testé le code un peu plus, je suis arrivé à une version qui fonctionne mieux. Je ne peux toujours pas garantir que cela fonctionnera dans toutes les circonstances.

+0

merci, j'ai utilisé votre modèle if/else pour restructurer mon code et je l'ai fait fonctionner: http://tanguay.info/web/index.php?pg=codeExamples&id=358 –

1

Vous devriez faire votre OutlineObject contiennent une liste des enfants OutlineObject s. De cette façon, vous pouvez lier à la collection enfant dans les vues arborescentes.

Regardez here pour un exemple. Ou here.


Pour l'analyse syntaxique, vous devez maintenir un Stack<OutlineObject> de vos objets imbriqués. Lorsque vous lisez OutlineObject suivant, regardez la profondeur du dernier OutlineObject dans la pile. Si votre niveau est plus grand, vous vous ajoutez comme un enfant de OutlineObject, et poussez votre OutlineObject sur la pile. Si votre niveau est le même, supprimez le OutlineObject et poussez votre objet à la place. Si votre niveau est plus grand, vous retirez cette pile supérieure OutlineObject et répétez la vérification.


En ce qui concerne votre changement d'ajouter

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

... ce code ne vérifie pas le cas lorsque le niveau de nouvel objet est inférieur au niveau de haut de la pile. Vous aurez besoin de:

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

oui OutlineObject a « Liste publique OutlineObjects {get; ensemble; } "et j'ai construit un ensemble de OutlineObjects récursifs à la main et je les ai liés avec succès à un TreeView, mais ce que je veux faire maintenant est de convertir une liste de texte dans cette collection récursive .. –

+0

vient de modifier le post, répondre à votre question – Vlad

+0

, cela m'a beaucoup plus loin, j'ai posté mon code ci-dessus, il a maintenant trois enfants racine mais les relations enfants/parents plus profondes ne sont pas correctes pour une raison quelconque, vont travailler dessus, merci –

0

Simple.

Créez une liste d'objets OutlineObject, un pour chaque niveau, ils serviront de parents.

Ainsi, l'algorithme:

  1. Créer l'objet de la ligne
  2. Trouver le niveau d'indentation (qui sera 0 pour les objets racine)
  3. Si la liste des parents a moins que le niveau +1 nombre d'éléments, ajoutez les éléments "null" jusqu'à ce qu'il en ait assez (ce qui signifie que pour le premier objet racine, ajoutez un élément "null" pour lui donner 1 élément)
  4. Remplacez l'élément #level dans cette liste par le nouvel objet que vous avez créé en 1. (puisque la liste est basée sur 0, les objets racine seront les premiers)
  5. Si le niveau est> 0, ajoutez comme un enfant aux parents [niveau 1], si le niveau == 0, ajoutez comme un objet racine

Cela devrait vous donner votre arbre structure. Vous devrez conserver une liste d'enfants dans chaque objet.

Notez également que la liste ci-dessus aura besoin erreur supplémentaire de vérifier si vous voulez gérer les erreurs dans le fichier, comme ceci:

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

Dans ce cas, le dernier enfant, il sera ajouté comme un enfant de "enfant 1", pas de "autre racine".

+0

droite, dans mon code je fais les étapes 1 et 2, mais à l'étape 3, que voulez-vous dire par" liste des parents ", je tiens une liste d'enfants dans chaque objet, voulez-vous garder piste d'une liste de parents aussi bien? –

+0

Non, pendant le processus de lecture, vous gardez simplement une liste des parents de chaque niveau. Chaque fois que vous rencontrez un nouvel objet racine (niveau 0), vous remplacez l'objet dans cette liste. Chaque fois que vous rencontrez un nouvel objet de niveau 1, vous utilisez l'objet de cette liste au niveau 0 comme parent. –

0

Le modèle composite est la première chose qui vient à l'esprit pour moi ...

0

Voici ma tentative qui est une combinaison de votre effort original et de l'approche de diamandiev. J'ai également ajouté une méthode Output() récursive qui reproduira efficacement le fichier d'entrée d'origine.

Malheureusement je ne pouvais pas vraiment comprendre l'approche de la pile, mais je serais intéressé de voir un exemple de travail.

Notez que ceci ne permet que votre exemple donné de nœuds imbriqués à 3 niveaux de profondeur. Tout plus que cela nécessitera une modification de la vérification 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

Quelle belle solution! Cela pourrait faire un petit utilitaire pratique. C'est parfait.

Je sais que cela fait un moment que vous avez publié ceci; Je n'ai pas pu trouver l'original mais j'ai trouvé une copie archivée here.

Je l'ai modifié un peu pour la brièveté et l'ai traduit en VB.NET pour ceux qui pourraient être intéressés.

est ici le résultat 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 

test

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 

Arbre

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 

Noeud

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 

Extensions

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 
Questions connexes