Добавить в сжатый поток - PullRequest
4 голосов
/ 28 октября 2010

Мне нужно решение, которое позволяет мне создавать сжатые файлы данных (gzip, zip. Tar и т. Д. - любой формат может работать), а затем свободно добавлять к ним данные без необходимости загружать весь файл в память и повторно сжимать его. (поиск во время распаковки также был бы замечательным). У кого-нибудь есть предложения по .NET?

Ответы [ 4 ]

2 голосов
/ 29 октября 2010

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

Чтобы добавить сжатый поток (возобновить сжатие), вам потребуется словарь в том состоянии, в котором он находился, когда сжатие было приостановлено.Алгоритмы сжатия не сохраняют словарь, потому что это было бы пустой тратой пространства - оно не нужно для распаковки;он снова создается из сжатого входа на этапе декомпрессии.

Я бы, вероятно, разделил вывод на куски, которые сжимаются отдельно.

1 голос
/ 29 октября 2010

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

Прежде всего, почему вы ищете программное решение, реализованное самостоятельно?

Вы можете просто разбить большой файл журнала на куски, то есть на почасовой или даже поминутной основе, и собирать их в отдельный словарь на ежедневной основе (чтобы не загромождать FS с огромным количеством файлов)в одном каталоге), поэтому вместо большого файла, который вам нужно обработать и найти, у вас будет много маленьких файлов, к которым можно быстро получить доступ по имени файла в сочетании с простыми правилами.Наличие большого файла - плохая идея (до тех пор, пока у вас не будет какой-то индекс), так как вам придется искать через него, чтобы найти подходящую информацию (например, по операции datetime), а операция поиска будет значительно длиннее.

Ситуациястановится еще хуже, когда начинает действовать сжатие, так как вам придется распаковывать данные для их поиска или создавать какой-то индекс.Нет необходимости делать это самостоятельно, вы можете включить сжатие папок в ОС и получить все преимущества сжатия прозрачно без какого-либо кодирования.

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

  • Разделение данных журнала на регулярной основе, например, по часамчтобы уменьшить производительность сжатия, нажмите
  • Включите сжатие папок ОС

Вот и все, вы уменьшите объем памяти.


Для продолжениясвой (в случае, если вы действительно этого хотите).Вы можете сделать то же самое, разбить данные на куски, сжать каждый и сохранить в вашем виде хранилища.Чтобы реализовать нечто подобное, я подумал бы о следующем:

  • сохранить один файл с необработанными (несжатыми) данными, в который вы будете записывать новую информацию;
  • сохранить и обновить индексный файл, напримерс сохраненными диапазонами дат на блок для быстрого поиска позиции файла в сжатых данных по дате;
  • сохранение файла для хранения сжатых данных, каждый блок в нем содержит свой размер и сжатые (например, с GZipStream) данные;

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


Также я бы посоветовал подумать , почему у вас такие большие файлы журналов.Вероятно, вы можете оптимизировать формат хранения.Например, если ваши журналы представляют собой текстовые файлы, вы можете переключиться в двоичный формат, например, создать словарь из исходных строк и хранить только идентификаторы сообщений вместо полных данных, например:

обновление региона 1;

обновление области 2;

сжатие данных;

сохранить как:

x1 1

x1 2

x2

Строки, приведенные выше только для примера, вы можете «распаковать» их во время выполнения по мере необходимости, переназначая данные обратно.Вы можете сэкономить много места, переключившись на двоичный код и, возможно, достаточно, чтобы забыть о сжатии.

У меня нет готовой реализации или алгоритма.Может быть, другие могут предложить лучше, но надеюсь, что мои мысли будут несколько полезны.

0 голосов
/ 27 ноября 2018

Нет расширенного тестирования, нет проверки ошибок, но этот небольшой тест скомпилирован и работает. Трюки были: а) deflator.FlushMode = FlushType.Full; (перед дефлятором. Напишите) б) выписывание буфера ДО конца использования, вызванного удалением, кажется, добавляет маркер конца к данным. Что, конечно, останавливает его добавление! в) Использование очень распространённого Ionic.Zlib.CF всего управляемого кода.

string m_FileCompressed;

void PartialFileTest()    {
    //m_FileCompressed = Application.persistentDataPath + "/" + "PartialWrite.dat";
    PartialFileWritePart("the quick brown fox ");
    PartialFileWritePart("jumps over the lazy dog!");
    string str = PartialFileReadAll();
}

System.Text.Encoding u8 = Encoding.UTF8;
void PartialFileWritePart(string str) {
    using (var ms = new MemoryStream()) {
        using (var deflator = new DeflateStream(ms, CompressionMode.Compress, true)){
            byte[]  s1 = u8.GetBytes(str);
            deflator.FlushMode = FlushType.Full;
            deflator.Write(s1, 0, s1.Length);
            deflator.Flush();
            CreateIfNeededAndAppendAllBytes(m_FileCompressed, ms.ToArray());
        }
    }
}

string PartialFileReadAll() {
    byte[] buf = new byte[100];
    using (var ms3 = new MemoryStream()){
        byte [] Bothparts = File.ReadAllBytes(m_FileCompressed);
        ms3.Write( Bothparts, 0, Bothparts.Length);
        using (var inflator = new DeflateStream(ms3, CompressionMode.Decompress)){
            ms3.Position = 0;
            inflator.Read(buf, 0, Bothparts.Length);
        }
    }
    return u8.GetString(buf);
}

byte [] ReadAllBytesIfExists(string path ) {
    if(!File.Exists(path)) return new byte[0];
    return File.ReadAllBytes(path);
}

public static void CreateIfNeededAndAppendAllBytes(string path, byte[] bytes) {
    if(!File.Exists(path)) File.Create(path).Dispose();
    using (var stream = new FileStream(path, FileMode.Append)) {
        stream.Write(bytes, 0, bytes.Length);
    }
}
0 голосов
/ 28 октября 2010

Вы видели GZipStream класс?Вы можете использовать его как любой другой поток.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...