簡體   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