2017-02-05 6 views
2

Чтобы обеспечить некоторый контекст, я пытаюсь оптимизировать код ниже , который считывает файл построчно, буферы эти строки и сохраняет в базу данных каждые 100 строк -Как считать вхождения символов в большом (5+ ГБ) файле с использованием C#?

using (StreamReader sr = new StreamReader(fileName, Encoding.Default)) 
{ 
    IList<string> list = new List<string>(); 
    int lineCount = 0; 
    foreach (var line in sr.ReadLines((char)someEOL)) //ReadLines is an extension method that yield returns lines based on someEOL while reading character by character 
    { 
     list.Add(line); //Keeping it simple for this example. In the actual code it goes through a bunch of operations 
     if(++lineCount % 100 == 0) { //Will not work if the total number of lines is not a multiple of 100 
      SaveToDB(list); 
      list = new List<string>(); 
     }  
    } 
    if(list.Count() > 0) 
     SaveToDB(list); //I would like to get rid of this. This is for the case when total number of lines is not a multiple of 100. 
} 

Как можно заметить, SaveToDB(list) происходит дважды в приведенном выше коде. Это необходимо во второй раз в случае total number of lines % 100 != 0 (например, если есть 101 строка, if(lineCount % 100 == 0) пропустит последний). Это не большая проблема, но мне интересно, смогу ли я от нее избавиться.

С этой целью, если бы я мог прочитать общее количество строк перед тем, как попасть в цикл foreach, я мог бы написать if(lineCount % 100 == 0) по-разному. Но для нахождения общего количества строк требуется пройти через символ файла по символу, чтобы считать someEOL, что является определенным, потому что размер файла может варьироваться от 5 до 20 ГБ. Есть ли способ сделать счет без штрафа за производительность (что кажется мне сомнительным, но, возможно, есть решение)? Или другой способ переписать его, чтобы избавиться от этого дополнительного звонка SaveDB(list)?

+0

Очень запутанным, что у вас есть проблемы с - рефакторинга кода может быть выполнено с помощью LINQ batching (http://stackoverflow.com/questions/13731796/create-batches-in-linq), но заголовок требует подсчета символов - поэтому дозатор не может ответить на ваш вопрос ... –

+0

@ AlexeiLevenkov Но как LINQ сможет создавать партии в этом сценарии? – Achilles

+0

Точно так же, как и с любым другим перечисляемым ... Не совсем уверен, что вас смущает? –

ответ

2

Ваш код выглядит отлично, за исключением создания новых пустых списков каждый раз, когда считывается 100 строк. В любом случае, вы можете попробовать этот подход:

var enumerator = sr.ReadLines((char)someEOL).GetEnumerator(); 
isValid = true; 

for (int i = 1; isValid; i++) 
{ 
    bool isValid = enumerator.MoveNext(); 

    if (isValid) 
    { 
     list.Add(enumerator.Current); 
    } 

    if (i % 100 == 0 || (!isValid && list.Count() > 0)) 
    { 
     SaveToDB(list); 

     // It is better to clear the list than creating new one for each iteration, given that your file is big. 
     list.Clear(); 
    } 
} 
+0

Не могли бы вы пояснить, как этот код решает рефакторинг, чтобы избежать второго вызова «SaveToDB»? –

+0

Я обновил свой ответ. Я не уверен, почему вам нелегко назвать это два раза, совершенно нормально, это такие ситуации. –

+0

@ GhasanAl-Sakkaf Хорошо, если это так. Я просто искал подтверждения, что нет никакой другой умной логики, которая могла бы решить ее лучше. – Achilles

1

Я думаю, что вы ищете StreamReader.Peek()

sr.Peek().Equals(-1) 

Код:

 string filepath = "myfile.txt"; 
     int lineCount = 0; 
     List<string> list = new List<string>(); 
     using (StreamReader sr = File.OpenText(filepath)) 
     { 
      string line; 
      while ((line = sr.ReadLine()) != null) 
      { 
       lineCount++; 
       if (lineCount % 100 == 0 || sr.Peek().Equals(-1)) 
       { 
        SaveToDB(list); 
        list = new List<string>(); 
       } 
      } 
     } 

 Смежные вопросы

  • Нет связанных вопросов^_^