2017-01-20 5 views
0

У меня есть код, который читает текст из 10 файлов размером примерно 80 МБ. Тем не менее, я не могу сделать это успешно, поскольку (в зависимости от того, как я пытался), он терпит неудачу на третьей-седьмой итерации. Пронумерованные строки - это способы, с помощью которых я пытался их прочесть, каждый из них терпит неудачу.Чтение из нескольких файлов вызывает исключение OutOfMemory

var lines = new List<string>(); 
var text = string.Empty; 
for (int i = 0; i < 10; i++) 
{ 
    try 
    { 
     //lines.AddRange(File.ReadAllLines(dirPath + string.Format(@"commands{0}.txt", i))); 
     //lines.Add(File.ReadAllText(dirPath + string.Format(@"commands{0}.txt", i))); 
     //lines.Add(text); 

     var bytes = File.ReadAllBytes(dirPath + string.Format(@"commands{0}.txt", i)); 
     text += Environment.NewLine + System.Text.Encoding.UTF8.GetString(bytes); 
    } 
    catch (Exception e) 
    { 
     //OutOfMemory exception 
    } 
} 

Что я делаю неправильно? Что именно закрывается? MB разрешено для приложения, длина строки, количество элементов в списке? И т.д.?

+0

Что вы делаете с этим «текстом» впоследствии? Возможно, было бы более удобно хранить его в виде байтового массива, если вы просто собираетесь его записать позже, или преобразовать массив байтов непосредственно в строку одновременно, а не в файл за раз. –

+1

Вы просто свободны от памяти. Вопрос слишком груб, чтобы предоставить альтернативу, никто не может понять, почему вам нужно хранить столько текста. Но это, безусловно, очень старомодная проблема. «Проект»> «Свойства»> «Вставить вкладку» и отключить опцию «Предпочитайте 32-разрядную». Ты не хочешь этого. –

ответ

1

text является объектом string, который имеет предел. What is the maximum possible length of a .NET string?

Вы можете использовать StringBuilder, который может увеличиться за пределы его, добавив к нему. https://msdn.microsoft.com/en-us/library/system.text.stringbuilder.maxcapacity(v=vs.110).aspx

Добавить using System.Text сперва.

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < 1000; i++) 
{ 
    var bytes = File.ReadAllBytes(dirPath + string.Format(@"commands{0}.txt", i)); 
    sb.Append(Environment.NewLine + System.Text.Encoding.UTF8.GetString(bytes)); 
} 
+0

10x80MB = 800 МБ, все еще намного меньше 2GB –

+0

OP должен использовать StringBuilder, но по разным причинам. –

+0

Вы правы. Мне просто было интересно, что файл с комбинированным содержимым файлов в какой-то мере превосходит. Я ждал, сможет ли OP вернуться назад с размещенными решениями. –

1

Проблема в string text. string является неизменным. Это означает, что при изменении string после создания будет создан новый объект string.

Делать это:

text += Environment.NewLine + System.Text.Encoding.UTF8.GetString(bytes); 

вы создаете объект в каждой итерации (даже больше, чем один объект - Environment.NewLine + System.Text.Encoding.UTF8.GetString(bytes); создает один объект, а затем вы text += создает еще один объект).

Предположим, вы прочитали первый файл, а затем добавить этот string текстом из второй - там будет храниться старый string, содержащий текст из файла и новый string, который содержит текст из двух файлов в памяти. Старые string хранятся, но не нужны.

Есть много памяти, которая не нужна, но сбор мусора еще не сделан (поэтому иногда вы получаете исключение на третьей итерации, иногда в 7-м - если GC происходит, вы идете дальше).

Чтобы избежать этого, используйте byte массив или StringBuilder вместо string.

Что касается List<string>:

Внутренне список содержит массив, а когда нет последовательной (смежной) области памяти для выделения для этого массива вы получите OutOfMemoryException тоже.

Вместо этого вы можете использовать LinkedList<string>.

Использование StringBuilder:

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < 10; i++) 
{ 
    try 
    { 
     var bytes = File.ReadAllBytes(dirPath + string.Format(@"commands{0}.txt", i)); 

     sb.Append(Environment.NewLine); 
     sb.Append(System.Text.Encoding.UTF8.GetString(bytes));  

     //avoid sb.Append(Environment.NewLine + System.Text.Encoding.UTF8.GetString(bytes)) 
     //because you still create unnecessary object doing concatenation (+)  
    } 
    catch (Exception e) 
    { 
     //OutOfMemory exception 
    } 
} 

//you can cast "sb" to "string" 
string res = sb.ToString(); 

Но вы должны рассмотреть вопрос о создании другого решения. Удержание 800 МБ в памяти не самое лучшее.