Определить размер файла, требуемый для сохранения списка объектов

170
28 января 2020, 17:10

Есть список объектов, который нужно сохранить в файл. Могу сохранять в файлы трех типов: текстовые, бинарные и xml. Нужен метод, который будет определять размер файлов, содержащих мой список объектов. Т.е. у меня есть список объектов, который я хочу сохранить в файл, я вызываю этот метод, а он мне сообщает: сколько памяти потребуется для текстового файла, сколько для бинарного и сколько для xml-файла. Поля моего класса:

[Serializable]
public class Equipment
{
    public string Department { get; set; }
    public string Name { get; set; }
    int amount;
    double unitCost;
    double totalCost;
}

Методы для записи файлов:

    public static void WriteTxtFile(List<Equipment> equipments, string path)
    {
        using (StreamWriter writer = new StreamWriter(path, false))
        {
            equipments.ForEach(equipment => writer.WriteLine($"{equipment.Department};{equipment.Name};{equipment.Amount};{equipment.UnitCost};{equipment.TotalCost}"));
        }
    }
    public static void WriteBinaryFile(List<Equipment> equipments, string path)
    {
        using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.OpenOrCreate)))
        {
            foreach(Equipment equipment in equipments)
            {
                writer.Write(equipment.Department);
                writer.Write(equipment.Name);
                writer.Write(equipment.Amount);
                writer.Write(equipment.UnitCost);
                writer.Write(equipment.TotalCost);
            }
        }
    }
    public static void WriteXmlFile(List<Equipment> equipments, string path)
    {
        XmlSerializer formatter = new XmlSerializer(typeof(List<Equipment>));
        using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
        {
            formatter.Serialize(fs, equipments);
        }
    }
Answer 1

Создайте свой класс, производный от Stream, который вместо записи данных будет считать их размер:

public class MyStream : Stream
{
    int c = 0;
    public override bool CanRead => false;
    public override bool CanSeek => false;
    public override bool CanWrite => true;
    public override long Length => с;
    public override long Position { get => c; set => throw new NotSupportedException(); }
    public override void Flush()
    {
        return;
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }
    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        c += count;
    }
}

Создайте для каждого метода перегрузку, которая будет писать те же данные, но не в файл, а в Stream:

public static void WriteTxtFile(List<Equipment> equipments, Stream s)
{
    StreamWriter writer = new StreamWriter(s);
    equipments.ForEach(equipment => writer.WriteLine($"{equipment.Department};{equipment.Name};{equipment.Amount};{equipment.UnitCost};{equipment.TotalCost}"));
    writer.Flush();
}

Тогда размер данных вы сможете определить так:

MyStream s = new MyStream();            
WriteTxtFile(eqlist, s);
Console.WriteLine("Text file size: " + s.Position);
Answer 2

есть список объектов, который я хочу сохранить в файл
сколько памяти потребуется для текстового файла, сколько для бинарного и сколько для xml-файла

Допустим, имеется следующий класс:

public class Equipment
{
    //public string Department { get; set; }
    //public string Name { get; set; }
    public int amount { get; set; }
    public double unitCost { get; set; }
    public double totalCost { get; set; }
}

И записывается он в бинарный файл с помощью BinaryWriter:

binaryWriter.Write(equipment.amount);
binaryWriter.Write(equipment.unitCost);
binaryWriter.Write(equipment.totalCost);

То есть данные идут вплотную, без дополнительной метаинформации.

Размер примитивных типов можно получить оператором sizeof. Следовательно, размер одного экземпляра и всего списка вычисляется следующим образом:

int equipmentSize = sizeof(int) + sizeof(double) + sizeof(double);
int listSize = equipmentSize * equipmentList.Count;

O(1) - эффективнее некуда.

Однако, если раскомментировать строковые свойства, то ситуация усложнится.

Длина строки в байтах зависит от используемой кодировки (BinaryWriter может использовать любую). Следовательно, нужно вычислять эту длину.

int nameSize = encoding.GetByteCount(equipment.Name);
int departmentSize = encoding.GetByteCount(equipment.Department);

где encoding - та же кодировка, что используется при записи в файл.

Естественно, длина строк у разных объектов в списке может быть разная, поэтому придётся сделать проход по всему списку и добавить сумму длин в listSize. Получаем O(n).

Дело осложняется тем, что при записи строк в файл BinaryWriter предваряет значение записываемой строки её длиной. Это значение тоже нужно приплюсовать к итоговому размеру. Причём длина рассчитывается как 7-bit encoded. Подробнее здесь и здесь.

Вычисление размера текстового файла зависит от используемой кодировки и от формата. Его сделать относительно несложно используя метод Encoding.GetByteCount.

XML-файл по сути является текстовым. Аналогично, его размер можно рассчитать аналитически, без самой записи. Но возникает много проблем с предварительным определением его формата: с индентацией и без, с переносом атрибутов на новую строку или без переноса и многое другое.

При желании, можно заморочиться ручным вычислением. Но это окупится лишь в том случае, если нужно рассчитывать размеры большого количества файлов/потоков большого размера. А главное, код в ответе MSDN.WhiteKnight именно это и делает.

READ ALSO
как реализовать в xnet SSLv3-compatible ClientHello запрос

как реализовать в xnet SSLv3-compatible ClientHello запрос

как реализовать в xnet SSLv3-compatible ClientHello запрос

161
Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение 127.0.0.1:8888

Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение 127.0.0.1:8888

Вот код, на другой машине все работало, сервер давал ответ, сейчас пишет

385
Как подружить List&lt;T&gt; и XAML?

Как подружить List<T> и XAML?

Я создал свой класс Client

162