[英]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.