Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用Linq解析文本数据文件_C#_.net_Linq_Parsing_Functional Programming - Fatal编程技术网

C# 用Linq解析文本数据文件

C# 用Linq解析文本数据文件,c#,.net,linq,parsing,functional-programming,C#,.net,Linq,Parsing,Functional Programming,我有一个记录的大文本文件,每个记录用一个换行符分隔。每个记录都有一个两位数的前缀,用于指定其类型。下面是一个例子: .... 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 6

我有一个记录的大文本文件,每个记录用一个换行符分隔。每个记录都有一个两位数的前缀,用于指定其类型。下面是一个例子:

....

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”s包含“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();
         }
     }
 }
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”的子行(但不幸的是忽略了“30”行本身)。此查询显然需要子查询(通过select)收集行并将其投影到适当的类型中,并使用适当的组合(包含InstrumentTypeGroup列表的ContractGroups等)


这个问题很可能归结为我缺乏函数式编程的经验,所以如果有人对这种解析有任何建议,那将是很有帮助的,谢谢-

我还不完全清楚你到底想做什么,但我如何解决这个问题,首先要编写一个
PartitionLines
函数,如下所示:

 public static IEnumerable<string> TextFileLineEnumerator()
 {
     using (StreamReader sr = new StreamReader("BigDataFile.txt"))
     {
         while (!sr.EndOfStream)
         {
             yield return sr.ReadLine();
         }
     }
 }
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”下的可枚举组,依此类推


这是未经测试的,可能会更干净,但我希望你能理解。

这样的文本文件通常在行中有一个分隔符。这似乎有一个制表符作为分隔符,您能确认或拒绝吗?如果您不确定,只需取一行并用
字符串拆分即可。拆分
,字符为
'\t'
。LINQ不是为此而设计的,请改用正则表达式。@leppie:我觉得正则表达式更适合标记字符串。如何使用正则表达式将继承人的体系结构文件解析为组合数据结构?我想您应该
返回currentGroup.ToArray()
或类似的内容,而不是
currentGroup
本身,因为否则OP可能会调用
分区行(s=>s.Substring(0,2),“30”)。ToList()
得到一大堆相同的
列表的实例
对象只有一组元素。Dan Tao,我同意,我只是匆忙地把它搞砸了。我认为最干净的方法是
currentGroup=newlist()
而不是清除它。我编辑了我的帖子。很好的解决方案。我确实遇到了Dan提到的关于重复列表实例的问题,但根据他的建议列出了该组,解决了该问题。谢谢你的努力!刚才看到的编辑,这也很有效,不会立即强制进行更可取的评估