2013-03-12 73 views
0

在此代碼中,字符串x給出OutOfMemoryException。有沒有其他的方法可以解析所有的文件而不需要OutofMemoryException?我嘗試過的代碼似乎沒有錯。在C#中處理「OutofMemoryException」?

有人建議讓程序通過文件讀取文件,而不是讀取整個文件並將其放入一個字符串x

IEnumerable<string> textLines = Directory.GetFiles(@"C:\Users\karansha\Desktop\Unique_Express\", "*.*") 
    .Select(filePath => File.ReadLines(filePath)) 
    .SelectMany(line => line); 

string x = string.Join(",", textLines); 
List<string> users = new List<string>(); 
Regex regex = new Regex(@"User:\s*(?<username>.*?)\s"); 
MatchCollection matches = regex.Matches(x); 
foreach (Match match in matches) 
{ 
    var user = match.Groups["username"].Value; 
    if (!users.Contains(user)) users.Add(user); 
} 
int numberOfUsers = users.Count(name => name.Length < 15); 
Console.WriteLine("Unique_Users_Express=" + numberOfUsers); 
+1

我們在這裏說幾個文件?文件也是巨大的? – 2013-03-12 13:22:15

+0

是的文件大小是巨大的。大約500 MB。 – 2013-03-12 13:23:36

+2

無論文件的大小,我也會建議處理另一個文件之後... – 2013-03-12 13:23:40

回答

5

您似乎很想將每個文件的所有行結合在一起。假設用戶名不交叉線,你可以在一個更清潔的方式做到這一點在一個單一的LINQ查詢:

var regex = new Regex(@"User:\s(?<username>[^\s]+)"); 
var path = @"C:\Users\karansha\Desktop\Unique_Express\"; 
var users = Directory.GetFiles(path, "*.*") 
        .Select(file => File.ReadLines(file)) 
        .SelectMany(lines => lines) 
        .SelectMany(line => regex.Matches(line).Cast<Match>()) 
        .Select(match => match.Groups["username"].Value) 
        .Distinct() 
        .ToList(); 

int numberOfUsers = users.Count(name => name.Length < 15); 
Console.WriteLine("Unique_Users_Express=" + numberOfUsers); 

希望查詢的每一行應該是清楚的。這將一次處理一行 - 只要你沒有那麼多的用戶,不同用戶名的簡單列表不適合內存,你應該沒問題。如果您需要需要計數,您甚至不需要致電ToList

請注意,我已經調整了一些實驗後的正則表達式 - 我希望對你沒有問題。

+0

將MatchCollection投射到Match就像選擇第一項? – 2013-03-12 13:36:43

+0

@Baboon:不,它只是通過轉換其中的每個元素,將實現非通用IEnumerable的MatchCollection轉換爲IEnumerable 。 – 2013-03-12 13:37:57

+0

因此,儘管'File.ReadLines'爲整個文件提供了一個枚舉,它一次只能讀取一行內存。如果你在「foreach」中發佈這些信息,這也會是真的嗎? – 2013-03-12 13:40:27

0

試試這個:假設用戶名不去其他行,你可以解析每一行並建立唯一的用戶名。我沒有試圖改變你的代碼。只是它的邏輯。

 IEnumerable<string> textLines = Directory.GetFiles(@"C:\Users\karansha\Desktop\Unique_Express\", "*.*") 
               .Select(filePath => File.ReadLines(filePath)) 
               .SelectMany(line => line); 

     List<string> users = new List<string>(); 

     textLines.ToList().ForEach(textLine => 
     { 
      Regex regex = new Regex(@"User:\s*(?<username>.*?)\s"); 
      MatchCollection matches = regex.Matches(textLine); 
      foreach (Match match in matches) 
      { 
       var user = match.Groups["username"].Value; 
       if (!users.Contains(user)) users.Add(user); 
      } 
     }); 

     int numberOfUsers = users.Count(name => name.Length < 15); 
     Console.WriteLine("Unique_Users_Express=" + numberOfUsers); 
+0

這仍然將所有*行的文本放到一個單獨的列表中。爲什麼要花費時間調用'ToList'和'ForEach',當你可以使用'foreach(textLine中的var textLine)'並獲得流媒體? – 2013-03-12 13:49:41

+0

@JonSkeet OP說他在字符串x = ...時遇到了內存異常,這是當他試圖將所有文件中的所有行一起加入時......我同意你的後續行使用傳統的foreach ...而不是轉換爲ToList()。 – 2013-03-12 14:01:25

+0

是的,OP本來是因爲處理數據的一種更糟糕的方式而失敗 - 但是沒有理由使用ToList。 – 2013-03-12 14:03:42