使用 LINQ 查询文本文件

发布于 2024-08-01 22:46:05 字数 1657 浏览 3 评论 0原文

我有一个简单的文本文件,其中包含一些具有以下结构的 CSV:

<前><代码>@Parent1_Field1、Parent1_Field2、Parent1_Field3 Child1_Field1、Child1_Field2 Child2_Field1、Child2_Field2 ...ETC。 @Parent2_Field1、Parent2_Field2、Parent2_Field3 Child1_Field1、Child1_Field2 Child2_Field1、Child2_Field2 ...ETC。

“@”表示紧邻其下的子对象的父对象。 (这可以使用 XML 更好地表示,但在我的情况下这不是一个选项。)

我的目的是使用 LINQ 查询该文件,而不将其全部内容加载到内存中。 首先,我创建了一个实现 IEnumerable 的类(此处为:MyCustomReader),在其中我使用 StreamReader 来获取文件的每一行。

例如,以下内容获取所有父对象(不包括子对象):

from line in MyCustomReader
where line.StartsWith("@")
select Parent.Create(line)

但是,当我想创建涉及父对象和子对象的查询时,我陷入了困境。 例如,获取特定父对象的所有子对象或获取特定子字段包含相同值的所有父对象。

例如,这获取特定 Parent 对象的所有子对象:

public IEnumerable<Child> GetChildrenForAParent(string uniqueParentName)
{
    Parent parent = null;
    foreach (string line in MyCustomReader)
    {
        if (line.StartsWith("@"))
            parent = Parent.Create(line);
        else if (parent.UniqueName == uniqueParentName)
            yield return Child.Create(line);
    }
}

第二个示例:

public IEnumerable<Parent> GetParentsWhereChildHasThisValue(string childFiledValue)
{
    Parent parent = null;
    foreach (string line in MyCustomReader)
    {
        if (line.StartsWith("@"))
        {
            parent = Line.Create(line);
        }
        else //child
        {
            Child child = Child.Create(line);
            if (child.FiledValue == childFiledValue)
                yield return parent;
        }
    }
}

如何使用 LINQ 实现这两个示例?

I have a simple text file containing some CSV with the following structure:

@Parent1_Field1, Parent1_Field2, Parent1_Field3
Child1_Field1, Child1_Field2
Child2_Field1, Child2_Field2
...etc.
@Parent2_Field1, Parent2_Field2, Parent2_Field3
Child1_Field1, Child1_Field2
Child2_Field1, Child2_Field2
...etc.

'@' indicates a parent object of child objects that are immediately below it. (This could be represented better using XML, but that's not an option in my case.)

My purpose is to use LINQ to query this file without loading its entire content into memory. First, I created a class (here: MyCustomReader) that implements IEnumerable in which I use StreamReader to get each line of the file.

E.g. the following gets all Parent objects (without the children):

from line in MyCustomReader
where line.StartsWith("@")
select Parent.Create(line)

However, I got stuck when I wanted to create queries that involve both Parent and Child objects. For instance, getting all the children for a particular parent object or getting all the Parent objects where a particular childfield contains the same value.

E.g. this gets all the children for a particular Parent object:

public IEnumerable<Child> GetChildrenForAParent(string uniqueParentName)
{
    Parent parent = null;
    foreach (string line in MyCustomReader)
    {
        if (line.StartsWith("@"))
            parent = Parent.Create(line);
        else if (parent.UniqueName == uniqueParentName)
            yield return Child.Create(line);
    }
}

and the second example:

public IEnumerable<Parent> GetParentsWhereChildHasThisValue(string childFiledValue)
{
    Parent parent = null;
    foreach (string line in MyCustomReader)
    {
        if (line.StartsWith("@"))
        {
            parent = Line.Create(line);
        }
        else //child
        {
            Child child = Child.Create(line);
            if (child.FiledValue == childFiledValue)
                yield return parent;
        }
    }
}

How could these two examples be achieved using LINQ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

万劫不复 2024-08-08 22:46:05

这并不漂亮,但对于第一个,类似以下内容应该有效:

MyCustomReader.SkipWhile(line => line != uniqueParentName).Skip(1).
                                     TakeWhile(line => !line.StartsWith("@"));

编辑:好的,所以我很无聊。 我认为这将为您解决第二个问题(但显然这不是一个适合 LINQ 的问题):

var res = MyCustomReader.Where(parentLine => parentLine.StartsWith("@"))
         .Join(MyCustomReader.Where(childLine => !childLine.StartsWith("@")),
              parentLine => parentLine,
              childLine => MyCustomReader.Reverse<string>()
                   .SkipWhile(z => z != childLine)
                   .SkipWhile(x => !x.StartsWith("@")).First(),
              (x, y) => new { Parent = x, Child = y })
         .Where(a => a.Child == childFiledValue).Select(a => a.Parent);

This isn't pretty but for the first one something like the following should work:

MyCustomReader.SkipWhile(line => line != uniqueParentName).Skip(1).
                                     TakeWhile(line => !line.StartsWith("@"));

EDIT: Ok so I'm bored. I think this will do the second one for you (however obviously its not a problem that is suited to LINQ):

var res = MyCustomReader.Where(parentLine => parentLine.StartsWith("@"))
         .Join(MyCustomReader.Where(childLine => !childLine.StartsWith("@")),
              parentLine => parentLine,
              childLine => MyCustomReader.Reverse<string>()
                   .SkipWhile(z => z != childLine)
                   .SkipWhile(x => !x.StartsWith("@")).First(),
              (x, y) => new { Parent = x, Child = y })
         .Where(a => a.Child == childFiledValue).Select(a => a.Parent);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文