2012-08-01 15 views
0

我有一個非常大的文件(> 1GB),它是管道分隔的。 我想使用file.readlines查詢文件,拆分每一行,根據索引列表(我將提供索引數組)從索引列中選擇特定元素,並將其返回給自定義類型類對象的字典。使用LINQ從文件中選擇一個字符串,對其進行分割並返回指定的成員

目前我有這樣的:

strIndexes = "1,3,5,7"; 
var selected = strIndexes.Split(',') 
         .Select(x => int.Parse(x)) 
         .Select(index => File.readlines(myFile) 
         .Select(x => split('|')[index]).toArray(); 

但是這種查詢是由索引領導,是不是有效的。 它返回4個矢量,每個用於上例中指定的4個索引。

我將不勝感激任何幫助。

更新:

謝謝大家的評論。 我正在添加一些示例數據。 文件數據看起來樣樣:

Line1: aa|ab|de|gt|hj|de|fr|gt|hy // Header 
Line2: sd|12|f4|tr|hj|df|ds|e3|12 
Line3: 34|fd|3d|35|df|45|dq|32|dd 
. 
. 
. 
Line N 34|df|f3|df|33|s2|23|df|44 

N =數百萬行。數據格式僅用於示例,每個單元格包含不同長度和結構的字符串。

現在,用戶以標題列名的形式輸入他想要的數據,例如:ab,de,fr,hy,我解析這個並理解我需要第2,3,7和9列。 現在我想解析文件並返回一個對象,其中每個條目都是一個字典條目,它表示一行中的數據,該行中包含一個鍵(這是一列數據)和一個值爲字符串[]的數據的標記化數據。

所以我們可以說這個例子,我想將數據按列7號進行排序,所以最終的字典是:

[ds]->[12,f4,12] 
[dq]->[fd,3d,dd] 
. 
. 
. 
[23]->[df,f3,44] 
+0

一兩件事,我會避免讀取該文件爲每個索引每次。 – Lee 2012-08-01 20:27:52

+1

很難遵循你想要達到的目標。如果您可以提供樣本輸入和期望的輸出,那麼幫助您會容易得多。 – 2012-08-01 20:28:55

+0

@Xander:我正確地裝入了'ReadLines'和'.ToArray()',並將'.ToArray()'調用移動到下一行以匹配語句的其餘部分。語義上有什麼變化? – 2012-08-01 20:29:10

回答

0

我建議編寫自己的標記生成器(或找到一個),你可以使用String.IndexOfAny並搜索'|'或',',建立你自己的狀態機,告訴它在哪裏放置這些值。

0

一些猜測,但也許這就是你想要做什麼:

// Split indexes string to integers 
var indexes = strIndexes.Split(',').Select(int.Parse); 

// Read file once 
var lines = File.ReadLines(myFile); 

// Split them (thank you, devundef!) 
var splitLines = lines.Select(line => line.Split('|')).ToArray(); 

// Create dictionary index => column array 
var dict = indexes.ToDictionary(
     index => index, 
     index => splitLines.Select(splitLine => splitLine[index]).ToArray() 
    ); 
+0

還有一個地方可以進行更多的優化:使用File.ReadLines進行拆分。 – devundef 2012-08-01 20:57:33

+0

@devundef是真的,我添加了它。 – Timbo 2012-08-01 21:01:29

+0

我在想這樣的事情:'var lines = File.ReadLines(myFile).Select(line => line.Split('|'))。ToArray();',所以你只需要枚舉一次並使用一半的記憶。對不起,我的鼻子上你的答案:) – devundef 2012-08-01 21:03:40

0

首先,分析你的索引字符串(strIndex)查詢之外,以避免重複該步驟多次:

string strIndexes = "7,2,3,9"; 
int[] indexes = Array.ConvertAll(strIndexes.Split(','), e => int.Parse(e) - 1); 

請注意,我已經將7放在第一位,因爲假設第一個索引是您的密鑰索引,從而簡化了所需的代碼。我還注意到你的索引在字符串中似乎是基於1的,這就是爲什麼我減去1來創建基於0的索引數組的原因。然後,這會產生你所要求的形式的Dictionary<string, string[]>

var selected = (from line in File.ReadLines(myFile) 
       let lineArray = line.Split('|') 
       select (from index in indexes 
       select lineArray[index])) 
       .ToDictionary(key => key.First(), value => value.Skip(1).ToArray()); 

運行此對您的4號線的樣本數據收益率:

[fr]->[ab, de, hy] 
[ds]->[12, f4, 12] 
[dq]->[fd, 3d, dd] 
[23]->[df, f3, 44] 

從這個代碼:

foreach (var item in selected) 
{ 
    Console.WriteLine("[{0}]->[{1}]", item.Key, string.Join(", ", item.Value)); 
} 
+0

更新了我的答案,以符合您的新的,更清晰的標準。 – JamieSee 2012-08-03 16:43:53

0

雖然你問對於linq解決方案,我想也許這不是一個好的做法,因爲你的文件很大,你將爲單個任務分配大量內存,甚至會發生OutOfMemoryException異常。

你可以只解析該文件中的每一行一次,每個索引提取值:

public Dictionary<int, List<String>> ParseFile(String fileName, int[] indexes) 
    var file = File.OpenText(myFile); 
    var dict = indexes.ToDictionary(i => i, i => new List<string>()); 

    while(!file.EndOfStream) 
    { 
     var line = file.ReadLine().Split('|'); 
     foreach(var entry in dict) 
      entry.Value.Add(line[entry.Key]); 
    } 
    file.Dispose(); 
    return dict; 
} 
相關問題