Страницы

Поиск по вопросам

среда, 4 марта 2020 г.

Максимально разумный размер файла xml для хранения данных

#c_sharp #xml #хранение_данных


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


Ответы

Ответ 1



XML - громоздкий формат, что ведёт к большому размеру файлов и большому потреблению места на носителях. Давайте рассмотрим способы уменьшения потребления ресурсов. Радикальный способ: рассмотрите возможность отказа от XML и перехода к другому формату файлов: это могут быть json, yaml, бинарные форматы, наподобие protobuf, и прочие. Кроме уменьшения размеров это может привести и к большей скорости обработки файлов (запись-чтение-парсинг), и к более быстрой передаче по сети. К тому же многие из них тоже являются общепринятыми стандартами, наряду с XML. Сжатие. Текстовые форматы почти всегда хорошо сжимаются архиваторами. XML не является исключением. Повторяющиеся теги, само собой, будут сжаты архиватором в один экземпляр. В дотнете легко и просто можно использовать GZipStream, DeflateStream. Конечно, можно применять и сжатие в форматы 7Z, Zip, RAR и т. п. Можно включить сжатие диска на уровне операционной системы (приложение даже не будет знать об этом). Естественно, сжатие применимо для любых форматов. P.S. Файлы офисного пакета: xlsx, docx - это зазипованный xml. Кодировка файлов. Для текстовых форматов выбор правильной кодировки является очень важным делом. Если взять однобайтовую ANSI - то размер файлов будет минимальный, но мы будем ограничены в количестве возможных символов. Соответственно, если нам нужны символы из разных алфавитов и другие символы за пределами восьми бит, придётся взять многобайтовую кодировку. Какую? Например, UTF-32: весь диапазон Юникода в нашем распоряжении! Однако, четыре байта на символ - не слишком ли расточительно? Стандартный и самый распространённый вариант - UTF-8. Пожалуй, эта кодировка оптиимальна для XML: часто повторяющиеся в нём символы: <, >, ", ', =, ? - будут кодироваться одним байтом. А если ещё и названия тегам и атрибутам давать английские, то они тоже будут кодироваться компактно. Однако, в некоторых случаях UTF-16 может оказаться выгодней: там где в UTF-8 на один символ может понадобиться три и более байтов, в UTF-16 будет достаточно двух. Но это нужно уточнять на деле, в зависимости от предполагаемых для хранения данных. А если ещё и вместо идиотского представления конца строки двумя символами LR/LF (\r\n), как зачем-то сделано в Windows, использовать один символ, как сделано в Unix/Linux/MacOS, то можно выиграть ещё несколько процентов. Тут вопрос в том, способны ли на это используемые сериализаторы/парсеры. Не указывать BOM. А что - экономим ещё пару байтов в каждом файле... Размер кластера файловой системы. Как известно, размер кластера может варьироваться в разных системах. Например, от 512 байт до 32 или даже 64 кб. Стандартный размер по умолчанию обычно 4 кб. Таким образом, если большинство файлов у нас имеют небольшой размер, не более нескольких сотен байтов (а самих файлов очень много), то на каждый из них будет выделено всё равно по одному кластеру: лишний расход места на харде очевиден. В данной ситуации может оказаться очень выгодно переформатировать диск под маленький размер кластера. И наоборот, если файлы у нас большие, минимум сотни килобайт, то для них будет выгоднее форматирование файловой системы под большие кластеры: чем меньше количество кластеров, тем меньше места отводится под файловую таблицу. К тому же, на больших кластерах происходит быстрее чтение-запись больших файлов. Нельзя не отметить, что архивирование довольно эффективно решает эту проблему: файлы в архиве хранятся последовательно как единое целое, поэтому маленькие файлы не будут занимать по целому кластеру. Причём можно применять даже архивирование без сжатия. Это всё были административные меры, применимые к любому формату и непосредственно не связанные с XML. Теперь переходим к нему самому. Следует отказаться от форматирования с индентацией. Такой формат удобнее для чтения человеком, но любой специализированный xml-редактор может на лету выполнять форматирование. Без отступов будет значительная экономия на пробельных символах. Bob 42 Bob42 В крайнем случае, использовать один TAB вместо нескольких пробелов. Вместо узлов-тегов можно использовать атрибуты: название будет представлено всего в одном экземпляре, вместо двух. Bob42 Ещё раз упомяну о желательности имён на английском языке: это может положительно сказаться на кодировании букв всего одним байтом в случае UTF-8. Хранение XML-схем. Иногда применяется помещение XmlSchema в файл XML с данными. Это зачастую довольно удобно, но при наличии большого количества однотипных файлов, валидируемых этой схемой, приводит к бездумному повторению одной и той же схемы во всех файлах. Следует хранить xsd в одном экземпляре, отдельно от xml-файлов с данными. Значения по умолчанию. Несложно догадаться, что если данные известны заранее, то их можно не сохранять в каждом файле. Кроме ручной реализации этого способа, при самостоятельной записи и чтении, есть и автоматические. Пример сериализации со значением по умолчанию. На помощь нам приходит XmlSerializer, с его способностью не записывать свойства, помеченные атрибутом DefaultValue (для десериализации дефолтные значения необходимо задавать в конструкторе): public class Person { public Person() { Gender = "female"; } public string Name { get; set; } [DefaultValue("female")] public string Gender { get; set; } } var list = new List { new Person { Name = "Bob", Gender = "male" }, new Person { Name = "Alice", Gender = "female" } }; xs.Serialize(Console.Out, list); Вариент с использованием XmlSchema, в которой можно указывать дефолтные значения (что является очень мощным способом, хоть и часто критикуемым). Bob 42 Alice 21 Видите суслика? А он есть! В xsd задано значение по умолчанию gopher для атрибута pet. Использовать короткие префиксы пространств имён. Наиболее частый неймспейс сделать namespace by default (префикса вообще не будет). Bob 42 Bob 42 Выкинуть все комментарии (по возможности). Alice 21 Для однотипных данных не нужны комментарии в каждом файле. Достаточно одного экземпляра описания в документации (схеме). Далее я мог бы рассказать о совсем уж специфичных способах работы с xml, приводящим к нехватке памяти при использовании потоковых (sic!) XmlReader/XmlWriter. Однако, сообщение уже превысило разумные пределы. Скажу лишь, что нужно заранее тщательно проектировать структуру xml, чтобы впоследствии можно было извлечь желаемые данные за один проход. Например, часто встречается нечто подобное: something 1 Данные нужно извлечь по индексу, который расположен в другой части файла. Вот и приходится либо в два прохода это делать, либо использовать словарь/хэшсет для аккумуляции информации. Не удержался. Ещё экзотический пример. Часто люди задаются вопросом: как дописывать информацию в файл xml? Вариант для извращенцев: var settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }; // Создаём файл xml. using (var fs = new FileStream("test.txt", FileMode.Create)) using (var writer = XmlWriter.Create(fs, settings)) { writer.WriteStartElement("root"); for (int i = 0; i < 3; i++) writer.WriteElementString("foo", i.ToString()); } // Дописываем информацию в конец файла. Без его перезаписи! using (var fs = new FileStream("test.txt", FileMode.Append)) using (var writer = XmlWriter.Create(fs, settings)) { writer.WriteComment("bar"); writer.WriteComment("baz"); } // Читаем из конца. var xml = XDocument.Load("test.txt"); var last = xml.LastNode; // Или так. var comments = xml.DescendantNodes().OfType(); foreach (var c in comments) Console.WriteLine(c.Value); Дописать информацию в начало xml-файла без его перезаписи? Да не вопрос! Опять на помощь приходит XmlSchema и значения по умолчанию. Предлагаю додуматься самостоятельно, как это сделать. А мне пора спать. Резюмируя: при должной сноровке можно комфортно (ну-у...) работать с файлами любого размера.

Комментариев нет:

Отправить комментарий