2

我正在使用 Visual Studio 和 NuGet 包 MoreLinq 作为我的以下解决方案。

我希望检索的文件的示例内容,该文件还包含其他不相关的数据:

...
#define HELLO
#include "hello.h"

code

#define BYE
#include "hello.h"
...

我对解决方案的尝试几乎完全符合我的要求。但几乎,我可以明白为什么,这是合乎逻辑的:

var files = from file in Directory.EnumerateFiles(path, ".", SearchOption.AllDirectories).Where(s => s.EndsWith(".c") || s.EndsWith(".h"))
            from line in File.ReadLines(file)
            .SkipWhile(l => l.TrimStart() != ("#define HELLO"))
            .TakeUntil(l => l.TrimStart() == ("#define BYE"))
            .ToList()
            select new
            {
                File = file,
                Line = line
            };

foreach (var f in files)
{
    sotredLines.Add(f.Line.Trim());
}

在这一点上,我的解决方案会给我以下结果:

#define HELLO
#include "hello.h"

code

#define BYE

如果您没有注意到,它缺少我也想检索的最后一行 -> #include "hello.h"。我解决这个问题的尝试是将以下行添加到代码中

...
.SkipWhile(l => l.TrimStart() != ("#define HELLO"))
.TakeUntil(l => l.TrimStart() == ("#define BYE"))
.TakeUntil(l => l.TrimStart() == ("#include \"hello.h\""))
...

但这(如预期)仅返回以下结果:

#define HELLO
#include "hello.h"

完全忽略了其他想要的信息。因为#include "hello.h"出现多次,并且它在找到的第一个处停止。

我只想从提到的文件中检索这些行,而不会丢失其中一行:

#define HELLO
#include "hello.h"

code

#define BYE
#include "hello.h"

对于仍在使用 Linq 的解决方案,请参阅下面的@Freggar 答案。

4

1 回答 1

2

您可以在其中设置一个标志TakeUntil,表明您已经过去#define BYE

bool byeFlag = false;
var p = from line in File.ReadLines(file)
        .SkipWhile(l => l.TrimStart() != ("#define HELLO"))
        .TakeUntil(l =>
        {
            bool ret = byeFlag;
            if (l.TrimStart() == "#define BYE")
            {
                byeFlag = true;
            }
            return ret;
        })
        .ToList()
        select new
        {
            File = file,
            Line = line
        };

但正如已经提到的,也许 LINQ 并不是你想要做的最好的工具。也许像ANTLR这样的解析器更适合这项工作?

于 2018-06-07T08:01:35.133 回答