Как правильно записать коллекцию содержащую большое количество данных в файл?
Помогите разобраться, есть коллекция List<Dictionary<string, string>>, в данном случаи количества словарей может достигать 8 000 000 штук, при этом в каждом содержится от 10 до 12 пар ключей и значений, как можно сохранить такую коллекцию в файл? Пробовал сериализовать в JSON, но для сериализации не хватает памяти, программа падает с ошибкой нехватки памяти, может кто-нибудь знает как решить такую проблему?
P. S. Файл нужен для последующего сравнения, без него не обойтись
Ответы (2 шт):
Для начала, не до конца понятно то, в каком формате вы хотите сохранять ваши данные. Будет ли это сырой формат в котором явно задаётся и разделяется отдельных словарь? или это будет XML? или это будет JSON?
Если предположить, что формат самый простой из возможных учитывая то что вы хотите это позже использовать для сравнения, то я могу порекомендовать следующие варианты оптимизации.
- Использовать класс
StreamWriterтаким образом вы создадите файл и откроете его для записи, запишете туда данные через буфер Windows и только потом закроете, это будет во много раз быстрее чем, скажем, использоватьSystem.IO.File.AppendAllText(...)т.к. вы не будете дёргать файл на открытие при каждой итерации. Тогда код будет выглядеть так:
List<Dictionary<string, string>> list = ...;
using (var sw = new StreamWriter(path))
{
foreach(var dict in list)
{
foreach(var kvp in dict)
{
sw.WriteLine(...); // формируем строку в файле
}
}
}
- Для формирования строки файла, использовать класс
StringBuilderон как раз может выполнять различные операции над строками эффективнее чем стандартные операторы над типомstring, хотя это становится менее читаемым кодом, как по мне.
StringBuilder sb = new StringBuilder();
foreach(var dict in list)
foreach(var kvp in dict)
{
sb.Clear();
sb.Append("Key: ");
sb.Append(kvp.Key);
sb.Append(",Value: ");
sb.Append(kvp.Value);
sw.WriteLine(sb.ToString());
}
Здесь не будет переполнений памяти, т.к. не создаётся множество лишних объектов, а stringbuilder очищается на каждой итерации. Можно так же добавить лимит на размер StreamWriter'a и когда тот достигает определённого размера, сбрасывать изменения на диск при помощи sw.Flush() Это всё что можно сказать учитывая отсутствие формата... но тут не сложно придумать как прикрутить формат.
Надеюсь это поможет в понимании :)
Удивлён, сколько тут советов, как объехать проблему на костылях, что конечно тоже может решить её, а нормальное и самое простое решение никто не подсказал.
Можно же сериализовать сразу в файловый поток, а не в строку:
// List<Dictionary<string, string>> data;
using var fs = File.Create("file.json");
await JsonSerializer.SerializeAsync(fs, data);
Точно так же можно десереализовать из файла. При работе с большими данными лучше забыть про вычитывание текста полностью в память, тем более в string.
Добавлю пояснение для тех, кому неочевидна суть решения.
string json = JsonSerializer.Serialize(data);
Вот этот способ требует выделения памяти под очень большую строку. Представьте, что она будет более миллиарда символов длиной, а под каждый символ нужно минимум 2 байта. То есть можно упереться в максимальную (по умолчанию) длину строки в байтах - 2 гигабайта, либо вообще закончится память на компьютере.
При сериализации в файловый поток выделение памяти для строки не требуется, так как запись при сериализации пойдет напрямую в файл. Следовательно и проблема, озвученная в вопросе
Пробовал сериализовать в JSON, но для сериализации не хватает памяти, программа падает с ошибкой нехватки памяти
будет решена.