2010-02-23 11 views
7

Come posso convertire questo testo contenuto del file in una collezione ricorsiva di oggetti che posso associare a un TreeView? cioè io voglio finire con una collezione di oggetti , il primo chiamato paesi che ha una collezione di tre oggetti figlio: france, germania, Italia, e così via ...Come posso convertire una lista di profili di file di testo in una raccolta di oggetti ricorsiva?

RISPOSTA: grazie a tutti coloro che hanno aiutato in questo, ecco il mio codice che analizza con successo questo schema di testo in un albero 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 

Il codice inferiore a è il massimo che ho ottenuto, ma non si tratta di più figli di genitori correttamente.

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

risposta

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

Questo dovrebbe fare il trucco. L'ho provato sul tuo file di testo. Potrebbero esserci alcuni bug. Provalo e racconta se funziona.

MODIFICA: in realtà dopo ulteriori test risulta che questo non funziona come previsto. È necessario aggiungere più logica per farlo funzionare. Lascio questo a te.

EDIT: Dopo aver testato il codice un po 'di più sono giunto a una versione che funziona meglio. Non posso ancora garantire che funzionerà in tutte le circostanze.

+0

grazie, ho usato il tuo pattern if/else per ristrutturare il mio codice e farlo funzionare: http://tanguay.info/web/index.php?pg=codeExamples&id=358 –

1

Si dovrebbe rendere il vostro OutlineObject contengono un elenco di bambino OutlineObject s. In questo modo è possibile associare la raccolta figlio nelle viste ad albero.

Look here per un esempio. O here.


Per l'analisi, si dovrebbe mantenere un Stack<OutlineObject> degli oggetti nidificati. Quando si legge il prossimo OutlineObject, esaminare la profondità dell'ultimo OutlineObject nello stack. Se il tuo livello è maggiore, aggiungi te stesso come figlio di quello OutlineObject e inserisci lo OutlineObject nello stack. Se il tuo livello è lo stesso, rimuovi quello OutlineObject e invia il tuo oggetto. Se il tuo livello è più grande, rimuovi lo stack superiore da OutlineObject e ripeti il ​​controllo.


Per quanto riguarda la modifica di aggiungere

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

... questo codice non verifica il caso in cui il livello di nuovo oggetto è più piccolo del livello di top stack. Avrai bisogno di:

... 
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 ha "Elenco pubblico OutlineObjects {get; impostato; } "e ho creato manualmente una serie di OutlineObjects ricorsivi e li ho associati correttamente a una TreeView, ma quello che voglio fare ora è convertire una lista di testo in quella raccolta ricorsiva: –

+0

ha appena modificato il post, rispondendo alla tua domanda – Vlad

+0

, che mi ha fatto molto più lontano, ho postato il mio codice sopra, ora ottiene tre figli root ma i rapporti figli/genitori più profondi non sono corretti per qualche ragione, lavoreranno su di esso, grazie –

0

Semplice.

Creare un elenco di oggetti OutlineObject, uno per ogni livello, che serviranno da genitori.

Quindi, l'algoritmo:

  1. creare l'oggetto dalla linea
  2. Trovare il livello di rientro (che sarà 0 per gli oggetti di root)
  3. Se l'elenco dei genitori ha meno di livello +1 numero di elementi, aggiungere elementi "null" fino a quando non ha abbastanza (il che significa che per il primo oggetto radice, aggiungere un elemento "null" per renderlo 1 elemento)
  4. Sostituire l'elemento numero # in quella lista con nuovo oggetto creato in 1. (poiché l'elenco è basato su 0, gli oggetti radice saranno i primi)
  5. Se il livello è> 0, aggiungerlo come un bambino ai genitori [di livello-1], se il livello di == 0, inserirlo come un oggetto radice

Questo dovrebbe dare il vostro albero struttura. Dovrai tenere un elenco di bambini in ciascun oggetto.

Si noti inoltre che l'elenco avrà bisogno di ulteriori errori controllando se si vuole gestire gli errori nel file, in questo modo:

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

In questo caso, l'ultimo figlio ci sarà aggiunto come un bambino di "bambino 1", non di "un'altra radice".

+0

right, nel mio codice sto facendo il punto 1 e 2, ma al punto 3, cosa intendi per" elenco dei genitori ", sto mantenendo un elenco di bambini in ogni oggetto, intendi mantenere traccia di un elenco di genitori pure? –

+0

No, durante il processo di lettura, è sufficiente tenere un elenco dei genitori di ogni livello. Ogni volta che incontri un nuovo oggetto root (livello 0), sostituisci l'oggetto in quell'elenco. Ogni volta che incontri un nuovo oggetto di livello 1, usi l'oggetto in quell'elenco al livello 0 come genitore. –

0

Il pattern Composite è la prima cosa che viene in mente per me ...

0

Ecco il mio tentativo che è una combinazione del tuo sforzo originale più l'approccio di diamandiev. Ho anche aggiunto un metodo ricorsivo Output() che riprodurrà efficacemente il file di input originale.

Purtroppo non riuscivo a capire l'approccio dello stack, ma sarei interessato a vedere un esempio funzionante.

Si noti che questo consente solo che il proprio esempio di nodi venga annidato a 3 livelli di profondità. Più di quello richiederà una modifica al controllo 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

Che ottima soluzione! Questo potrebbe rendere una pratica piccola utilità. È perfetto.

So che è passato un po 'di tempo da quando hai postato questo; Non ero in grado di individuare l'originale, ma ho trovato una copia archiviata here.

L'ho modificato un po 'per brevità e tradotto in VB.NET per coloro che potrebbero essere interessati.

Ecco il risultato finale:

principale

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 

prova

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 

Albero

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 

estensioni

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 
Problemi correlati