Как я могу преобразовать это содержание текстового файла в рекурсивный набор объектов, которые я могу связать с TreeView? т.е. Я хочу закончить с набором 3 объектов, первый, названный странами, который имеет набор трех дочерних объектов: Франция, Германия, Италия, и так далее...
ОТВЕТ: благодаря все, кто выручил на этом, вот являются моим кодом, который успешно анализирует эту текстовую схему в дерево 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
Код ниже - насколько я получил его, но он не имеет дело с несколькими детьми родителей правильно.
using System;
using System.Collections.Generic;
using System.Text;
namespace TestRecursive2342
{
class Program
{
static void Main(string[] args)
{
List outlineObjects = new List();
//convert file contents to object collection
List lines = Helpers.GetFileAsLines();
Stack stack = new Stack();
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 OutlineObjects { get; set; }
public string Line { get; set; }
public int Indent { get; set; }
public OutlineObject(string rawLine)
{
OutlineObjects = new List();
Indent = rawLine.CountPrecedingDashes();
Line = rawLine.Trim(new char[] { '-', ' ', '\t' });
}
}
public static class Helpers
{
public static List GetFileAsLines()
{
return new List {
"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;
}
}
}
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();
}
Это должно помочь. Я проверил это на вашем текстовом файле. Могут быть какие-то ошибки. Проверьте это и скажите, работает ли он.
РЕДАКТИРОВАТЬ: На самом деле после дальнейшего тестирования выяснилось, что это не работает должным образом. Вам нужно добавить больше логики, чтобы она работала. Я оставляю это на ваше усмотрение.
РЕДАКТИРОВАТЬ: Еще немного протестировав код, я пришел к версии, которая работает лучше. Я все еще не могу гарантировать, что он будет работать при любых обстоятельствах.
Вы должны сделать ваш OutlineObject
списком дочерних OutlineObject
s. Таким образом, вы можете привязаться к дочерней коллекции в древовидных представлениях.
См. Пример здесь . Или здесь .
Для синтаксического анализа вы должны поддерживать Stack
ваших вложенных объектов.
Когда вы читаете следующий OutlineObject
, посмотрите на глубину последнего OutlineObject
в стеке. Если ваш уровень выше, вы добавляете себя как потомок этого OutlineObject
и помещаете свой OutlineObject
в стек. Если ваш уровень такой же, вы удаляете этот OutlineObject
и вместо этого нажимаете свой объект. Если ваш уровень больше, вы удалите верхний стек OutlineObject
и повторите проверку.
Относительно изменения добавления
if (topObject.Indent
... этот код не проверяет случай, когда уровень нового объекта меньше уровня вершины стека. Вам понадобится:
...
else if (topObject.Indent == oo.Indent)
{
stack.Pop();
stack.Push(oo);
}
else
{
while (stack.Top().Indent >= oo.Indent)
stack.Pop();
stack.Push(oo);
}
Просто.
Создайте список объектов OutlineObject, по одному для каждого уровня, они будут служить родительскими объектами.
Итак, алгоритм:
Это должно дать вам древовидную структуру. Вам нужно будет вести дочерний список в каждом объекте.
Также обратите внимание, что в приведенном выше списке потребуется дополнительная проверка ошибок, если вы хотите, чтобы он обрабатывал ошибки в файле, например:
root
-child 1
--child 2
another root
--child 3 (note that we skipped a level)
В этом случае последний дочерний элемент будет добавлен как дочерний элемент «child 1» , а не "другого корня".
Шаблон Composite - это первое, что приходит мне на ум...
Вот моя попытка, которая представляет собой комбинацию ваших оригинальных усилий и подхода Диамандиева. Я также добавил рекурсивный метод Output (), который эффективно воспроизводит исходный входной файл.
К сожалению, я не мог полностью понять стековый подход, но мне было бы интересно увидеть рабочий пример.
Обратите внимание, что это позволяет только для данного примера узлов, вложенных на 3 уровня в глубину. Любое большее, чем это, потребует модификации проверки 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;
}
}
}