读取空格缩进List <string> </string>

时间:2014-06-17 09:14:32

标签: c# text recursion indentation

我有一个List,其中列表的内容用空格缩进。

有两列(控件类型和ID),列有固定长度。

缩进表示元素之间的父子关系。

举个例子:

    Modal                    000
     Child1                  100    
      Child1                 110
     Child2                  200
      Child1                 210
      Child2                 220
       Child1                221
     Child3                  300

要保存信息,我有以下简单类:

class CustomDefinition
{
    public List<CustomControl> Children { get; set; }
}

class CustomControl
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string Name { get; set; }
    public List<CustomControl> Children { get; set; }
}

计划是拥有一个CustomDefinition类的实例,它将包含根元素,而其他元素(根元素的子元素)将位于根本身的子集合中。根元素的ParentId000,而子元素应该接收其父元素的id。

由于孩子们自己有孩子,他们将被安置在自己的孩子们的收藏中。

我在这里找到了一个扩展方法,它为我提供了第一个非空格字符的索引,但我不知道应该如何继续:

    private static void _TestParse(List<string> items)
    {
        CustomDefinition definition = new CustomDefinition() {
            Children = new List<CustomControl>()
        };

        int currentControlId = 0;

        for (int i = 0; i < items.Count; i++)
        {
            // the current element
            var control = items[i];

            // the aforementioned extension method
            var controlIndex = control.IndexOf<char>(c => !char.IsWhiteSpace(c));

            // fixed length for the first column
            var controlName = control.Substring(0, 20).Trim();      

            // fixed length for the second column
            var controlId = control.Substring(20, 3);

            int.TryParse(controlId, out currentControlId);

            definitions.Children.Add(new CustomControl() {
                Id = currentControlId,
                Name = controlName
            });

            // i need to find if the current element has children
            // (in the example above the root element has 3 children)
            // then add these children to the current element

            var currentElement = definitions.Children
                .Find(x => x.Id == currentControlId);

            // then do this for all children and their children?!
        }
    }

我希望这一切都有意义,我感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

如果我从这些数据开始:

var data = new []
{
    "Modal                    000",
    " Child1                  100",
    "  Child1                 110",
    " Child2                  200",
    "  Child1                 210",
    "  Child2                 220",
    "   Child1                221",
    " Child3                  300",
};

然后我可以创建一个这样的原始项目列表:

var items =
    from x in data
    let idText = x.Substring(20)
    let indentedText = x
        .Substring(0, x.Length - idText.Length)
        .TrimEnd()
    let name = indentedText.Trim()
    let indent = indentedText.Length - name.Length
    select new
    {
        id = int.Parse(idText),
        name,
        indent,
    };

这让我:

items

现在我可以使用此代码将其转换为所需的对象模型:

var itemDictionary = new Dictionary<int, CustomControl>();

foreach (var item in items)
{
    itemDictionary[item.indent] = new CustomControl()
    {
        Id = item.id,
        Name = item.name,
        Children = new List<CustomControl>(),
    };
    if (item.indent != 0)
    {
        itemDictionary[item.indent].ParentId =
            itemDictionary[item.indent - 1].Id;
        itemDictionary[item.indent - 1]
            .Children.Add(itemDictionary[item.indent]);
    }
}

然后我得到了这个输出:

result

答案 1 :(得分:0)

Main方法中,我创建了示例数据,初始化了类,并对数据进行了解析。有一个递归方法负责搜索当前节点的父节点。我不得不修改你的CustomControl课程。在示例中查看ID的结构,将它们保存为字符串会更加明显(如果您喜欢它们,则将其恢复为int - 只需要稍微修改我的代码)。我还在CustomControl添加了一个属性。它负责记住项目的缩进级别。

代码中的更多解释作为注释。代码下方:

    static void Main(string[] args)
    {
        // Create sample data
        List<string> items = new List<string>();
        items.Add("Modal                    000");
        items.Add(" Child1                  100");
        items.Add("  Child1                 110");
        items.Add(" Child2                  200");
        items.Add("  Child1                 210");
        items.Add("  Child2                 220");
        items.Add("   Child1                221");
        items.Add(" Child3                  300");

        // Initialize the collection object
        CustomDefinition customDefinition = new CustomDefinition();
        customDefinition.Children = new List<CustomControl>();

        // Keep reference to the previous item in the list
        CustomControl prevItem = null;

        for (int i = 0; i < items.Count; ++i)
        {
            // Find indentation level
            int currentIndent = items[i].TakeWhile(c => char.IsWhiteSpace(c)).Count();
            string[] itemIdPair = items[i].Trim().Split(' ').ToArray();

            // Create CustomControl from data
            CustomControl currentItem = new CustomControl();
            // Before split the string is trimmed so name is always first item and ID - last one
            currentItem.Id = (itemIdPair[itemIdPair.Length -1]);
            currentItem.Name = itemIdPair[0];
            currentItem.IndentLevel = currentIndent;

            // Add CustomControl reference to the collection
            customDefinition.Children.Add(currentItem);

            // First item - main node
            if (prevItem == null)
            {
                prevItem = currentItem;
            }
            // Siblings - get the same parent
            else if (currentItem.IndentLevel == prevItem.IndentLevel)
            {
                CustomControl parent = customDefinition.Children.Where(item => item.Id == prevItem.ParentId).FirstOrDefault();
                currentItem.ParentId = parent.Id;
                if (parent.Children == null)
                    parent.Children = new List<CustomControl>();
                parent.Children.Add(currentItem);
            }
            // Child
            else if (currentItem.IndentLevel > prevItem.IndentLevel)
            {
                currentItem.ParentId = prevItem.Id;
                if (prevItem.Children == null)
                    prevItem.Children = new List<CustomControl>();
                prevItem.Children.Add(currentItem);
            }
            // Item's level is higher than the previous item - find how far does it go and
            // get the parent for the item
            else
            {
                // parent's indentation will be one level higher than the current one (currentIndent - 1)
                // try to find it recursively
                // Use the previous node in the list of nodes as the starting point
                CustomControl parent = GoBackToLevel(customDefinition.Children, prevItem, currentIndent - 1);
                currentItem.ParentId = parent.Id;
                if (parent.Children == null)
                    parent.Children = new List<CustomControl>();
                parent.Children.Add(currentItem);
            }

            // Replace the reference to the previous item.
            prevItem = currentItem;
        }
    }

    private static CustomControl GoBackToLevel(List<CustomControl> items, CustomControl start, int parentLevel)
    {
        // Find a parent of the starting CustomControl using the control's ID.
        CustomControl parent = items.Where(item => item.Id == start.ParentId).FirstOrDefault();

        // Does the parent has the expected indentation level? Yes - get the parent
        // else use the parent as the starting point for search instead.
        if (parent.IndentLevel == parentLevel)
            return parent;
        else
            return GoBackToLevel(items, parent, parentLevel);
    }

修改后的CustomControl

class CustomControl
{
    public string Id { get; set; }
    public string ParentId { get; set; }
    public string Name { get; set; }
    public List<CustomControl> Children { get; set; }
    public int IndentLevel { get; set; }
}