繁体   English   中英

用Linq解析文本数据文件

[英]Parsing Text Data File With Linq

我有一个大的记录文本文件,每个记录都由换行符分隔。 每条记录均以两位数字作为前缀,以指定其类型。 这是一个例子:

....

30AA ALUMINIUM ALLOY     LMELMEUSD2.00  0.35         5101020100818
40AADFALUMINIUM ALLOY USD USD 100   1       0.20000    1.00   0 100  140003
50201008180.999993  0.00  0.00  120100818
60       0F     1  222329 1.000000      0      0  -4667  -4667   4667   4667
50201008190.999986  0.00  0.00  120100819
60       0F     1  222300 1.000000      0      0  -4667  -4667   4667   4667
40AADOALUMINIUM ALLOY USD USD 100   1       0.20000    1.00   0 100  140001
50201009150.000000  0.17  0.17  120100915
60    1200C     1  101779 0.999800      0      0  -4666  -4666   4665   4665
60    1200P     1       0 0.000000      0      0      0      0      0      0
60    1225C     1   99279 0.999800     -1     -1  -4667  -4667   4665   4665
60    1225P     1       0 0.000000      0      0      0      0      0      0
60    1250C     1   96780 0.999800      0      0  -4666  -4666   4665   4665
60    1250P     1       0 0.000000      0      0      0      0      0      0
60    1275C     1   94280 0.999800     -1     -1  -4667  -4667   4665   4665
60    1275P     1       0 0.000000      0      0      0      0      0      0
60    1300C     1   91781 0.999800      0      0  -4666  -4666   4665   4665
60    1300P     1       0 0.000000

.......

该文件包含基于两位数字前缀的层次结构关系。 您可以将包含“ 40”行的“ 30”行视为其子级。 “ 40”行包含“ 50”,“ 50”行包含“ 60”。 解析后,这些行及其关联的前缀显然将映射为clr类型,“ 30”映射为“ ContractGroup”,“ 40”映射为“ InstrumentTypeGroup”,“ 50”映射为“ ExpirationGroup”。

我试图采用一种实用的方法进行解析,并通过延迟加载方法减少内存消耗,因为此文件非常大。 我的第一步是创建一个生成器,一次生成一行,如下所示:

 public static IEnumerable<string> TextFileLineEnumerator()
 {
     using (StreamReader sr = new StreamReader("BigDataFile.txt"))
     {
         while (!sr.EndOfStream)
         {
             yield return sr.ReadLine();
         }
     }
 }

这使我可以针对文本文件使用Linq,并将这些行作为流处理。

我的问题是尝试将此流处理为它的成分收集结构,这是第一次尝试:

  var contractgroups =   from strings in TextFileLineEnumerator()
                          .SkipWhile(s => s.Substring(0, 2) != "30")
                            .Skip(1) where strings.Substring(0,2) != "30"
                              select strings;

这给了我所有的“ 30”子行(但不幸的是省略了“ 30”行本身。)显然,此查询将需要子查询来收集(通过选择)这些行并将其投影到它们的适当类型中,并具有适当的组成(ContractGroups包含InstrumentTypeGroups等的列表)

这个问题很可能归结为我对函数式编程缺乏经验,因此,如果有人对此类解析有任何指示,那将是有帮助的,谢谢-

对我来说,您到底想做什么并不完全清楚,但是我要如何解决这个问题将是首先编写一个PartitionLines函数,如下所示:

public static IEnumerable<IEnumerable<string>> PartitionLines(
    this IEnumerable<string> source,
    Func<string, string> groupMarkerSelector,
    string delimeter)
{
    List<string> currentGroup = new List<string>();

    foreach (string line in source)
    {
        var key = groupMarkerSelector(line);
        if (delimeter == key && currentGroup.Count > 0)
        {
            yield return currentGroup;
            currentGroup = new List<string>();
        }

        currentGroup.Add(line);
    }

    if (currentGroup.Count > 0)
        yield return currentGroup;
}

(请注意,我的函数有时将一个“组”加载到内存中;我认为这是可以的。)

然后我会采取这样的事情:

var line30Groups =
    TextFileLineEnumerator().
    PartitionLines(l => l.Substring(0, 2), "30");

现在,您已经将这些行分成几组,每次看到“ 30”时,就会出现一组新的行。 您可以进一步细分:

var line3040Groups =
    TextFileLineEnumerator().
    PartitionLines(l => l.Substring(0, 2), "30").Select(g =>
        g.PartitionLines(l => l.Substring(0, 2), "40"));

现在,您已经在“ 30”下的组中找到了行,并且每个组都是每个“ 40”下的组的枚举。 等等。

这未经测试,可能会更清洁,但我希望您能明白。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM