Большие строки: System.OutOfMemoryException - PullRequest
2 голосов
/ 07 марта 2011
var fileList = Directory.GetFiles("./", "split*.dat");
int fileCount = fileList.Length;
int i = 0;
foreach (string path in fileList)
{
    string[] contents = File.ReadAllLines(path); // OutOfMemoryException
    Array.Sort(contents);
    string newpath = path.Replace("split", "sorted");
    File.WriteAllLines(newpath, contents);
    File.Delete(path);
    contents = null;
    GC.Collect();

    SortChunksProgressChanged(this, (double)i / fileCount);
    i++;
}

И для файла, который состоит из ~ 20-30 больших строк (каждая строка ~ 20 МБ), у меня есть OutOfMemoryException, когда я выполняю метод ReadAllLines.Почему возникает это исключение?И как мне это исправить?PS Я использую Mono на MacOS

Ответы [ 3 ]

7 голосов
/ 07 марта 2011

Вы всегда должны быть очень осторожными при выполнении операций с потенциально неограниченными результатами.В вашем случае чтение файла.Как вы упоминаете, размер файла и / или длина строки не ограничены.

Ответ заключается в чтении «достаточного» количества строк для сортировки, затем пропуска символов до следующей строки и прочтении следующего «достаточно».Возможно, вы захотите создать поиск по строковому индексу так, чтобы при достижении неоднозначного порядка сортировки строк вы могли вернуться назад, чтобы получить больше данных из строки (поиск в позиции файла).Когда вы вернетесь назад, вам нужно только прочитать следующий сортируемый кусок, чтобы устранить неоднозначность конфликтующих строк.

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

Встроенная сортировка работает не так быстро, как хотелось бы.

Примечание:

  1. Если вы звоните в GC. * вы, вероятно,сделал это неправильно
  2. настройка содержимого = ноль не поможет вам
  3. Если вы используете foreach и поддерживает индекс, то вам может быть лучше с for (int i ...) для читабельности
2 голосов
/ 07 марта 2011

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

Попробуйте создать класс, представляющий абстракцию над строкой, например, обернув начальный индекс и конечный индекс этоголиния.Когда вы позволяете этому классу реализовать IComparable<T>, он позволяет вам сортировать эту строку с другими строками.Опять же, хитрость заключается в том, чтобы иметь возможность читать символы из файла по одному.Вам нужно будет напрямую работать с Stream s (File.Open).

Когда вы сделаете это, вы сможете написать свой код приложения следующим образом:

List<FileLine> lines = GetLines("fileToSort.dat");

lines.Sort();

foreach (var line in lines)
{
    line.AppendToFile("sortedFile.dat");
}

Вашзадача будет заключаться в реализации GetLines(string path) и создании класса FileLine.

Обратите внимание, что я предполагаю, что фактическое количество строк будет достаточно маленьким, чтобы List<FileLine> поместился в памяти (что означает приблизительныймаксимум 40 000 000 строк).Если количество строк может быть выше, вам даже потребуется более гибкий подход, но поскольку речь идет о 20–30 строках, это не должно быть проблемой.

0 голосов
/ 07 марта 2011

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

Как вы сказали:

Я должен реализовать внешнюю сортировку иПокажите моему учителю, что он работает с файлами, размер которых превышает объем моей оперативной памяти.

Хорошо, так как, по вашему мнению, вы когда-нибудь прочтете файл;) Это сделано специально.ReadAllLiens НЕ реализует добавочную внешнюю сортировку.В результате дует.

...