Подтвердить что ты не робот

Сохранение словаря <String, Int32> в С# - Сериализация?

Я пишу приложение С#, которое должно читать около 130 000 (String, Int32) пар при запуске в словаре. Эти пары сохраняются в файле .txt и, таким образом, легко изменяются кем-либо, что является чем-то опасным в контексте. Я хотел бы спросить, есть ли способ сохранить этот словарь, чтобы можно было безопасно хранить информацию без потери производительности при запуске. Я попытался использовать BinaryFormatter, но проблема в том, что, хотя исходная программа занимает от 125 мс до 250 мс при запуске, чтобы читать информацию из txt и строить словарь, десериализация результирующих двоичных файлов занимает до 2 с, что не слишком само по себе, но по сравнению с первоначальной производительностью - 8-16-кратное снижение скорости.

Примечание. Шифрование важно, но наиболее важным должен быть способ сохранения и чтения словаря с диска - возможно, из двоичного файла - без использования Convert.ToInt32 на каждой строке, что повышает производительность.

4b9b3361

Ответ 1

интересный вопрос. Я сделал несколько быстрых тестов, и вы правы - BinaryFormatter на удивление медленнее:

  • Сериализовать 130 000 словарных статей: 547 мс
  • Deserialize 130,000 записей в словаре: 1046ms

Когда я закодировал его с помощью StreamReader/StreamWriter с значениями, разделенными запятыми, я получил:

  • Сериализовать 130 000 записей в словаре: 121 мс
  • Deserialize 130,000 записей в словаре: 111 мс

Но потом я попробовал просто использовать BinaryWriter/BinaryReader:

  • Сериализовать 130 000 словарных статей: 22 мс
  • Deserialize 130,000 записей в словаре: 36 мс

Код для этого выглядит следующим образом:

public void Serialize(Dictionary<string, int> dictionary, Stream stream)
{
    BinaryWriter writer = new BinaryWriter(stream);
    writer.Write(dictionary.Count);
    foreach (var kvp in dictionary)
    {
        writer.Write(kvp.Key);
        writer.Write(kvp.Value);
    }
    writer.Flush();
}

public Dictionary<string, int> Deserialize(Stream stream)
{
    BinaryReader reader = new BinaryReader(stream);
    int count = reader.ReadInt32();
    var dictionary = new Dictionary<string,int>(count);
    for (int n = 0; n < count; n++)
    {
        var key = reader.ReadString();
        var value = reader.ReadInt32();
        dictionary.Add(key, value);
    }
    return dictionary;                
}

Как говорили другие, хотя, если вы обеспокоены тем, что пользователи манипулируют файлом, шифрование, а не двоичное форматирование - это путь вперед.

Ответ 2

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

Подробнее см. Шифрование и расшифровка строки.

Ответ 3

Шифрование происходит за счет управления ключами. И, конечно же, даже самые быстрые алгоритмы шифрования/дешифрования медленнее, чем вообще никакого шифрования. То же самое со сжатием, что поможет, только если вы связаны с I/O.

Если производительность ваша главная забота, начните смотреть на то, где на самом деле узкое место. Если виновником действительно является вызов Convert.ToInt32(), я полагаю, что вы можете напрямую хранить биты Int32 и избегать простого акта, который должен быть быстрее, чем синтаксический анализ строкового значения. Чтобы обфускать строки, вы можете xor каждый байт с некоторым фиксированным значением, которое быстро, но не обеспечивает ничего более, чем дорожный альбом для определенного атакующего.

Ответ 4

Ну, использование BinaryFormatter на самом деле не является безопасным способом хранения пар, так как вы можете написать очень простую программу для десериализации (после того, как, скажем, запустите отражатель вашего кода, чтобы получить тип)

Как насчет шифрования txt? Что-то вроде this например? (для максимальной производительности, попробуйте без сжатия)

Ответ 5

Возможно, что-то вроде:

    static void Serialize(string path, IDictionary<string, int> data)
    {
        using (var file = File.Create(path))
        using (var writer = new BinaryWriter(file))
        {
            writer.Write(data.Count);
            foreach(var pair in data)
            {
                writer.Write(pair.Key);
                writer.Write(pair.Value);                    
            }
        }
    }
    static IDictionary<string,int> Deserialize(string path)
    {
        using (var file = File.OpenRead(path))
        using (var reader = new BinaryReader(file))
        {
            int count = reader.ReadInt32();
            var data = new Dictionary<string, int>(count);
            while(count-->0) {
                data.Add(reader.ReadString(), reader.ReadInt32());
            }
            return data;
        }
    }

Обратите внимание, что это не делает никакого повторного шифрования; это отдельная проблема. Вы также можете обнаружить, что добавление дефляции в микс уменьшает количество файлов IO и увеличивает производительность:

    static void Serialize(string path, IDictionary<string, int> data)
    {
        using (var file = File.Create(path))
        using (var deflate = new DeflateStream(file, CompressionMode.Compress))
        using (var writer = new BinaryWriter(deflate))
        {
            writer.Write(data.Count);
            foreach(var pair in data)
            {
                writer.Write(pair.Key);
                writer.Write(pair.Value);                    
            }
        }
    }
    static IDictionary<string,int> Deserialize(string path)
    {
        using (var file = File.OpenRead(path))
        using (var deflate = new DeflateStream(file, CompressionMode.Decompress))
        using (var reader = new BinaryReader(deflate))
        {
            int count = reader.ReadInt32();
            var data = new Dictionary<string, int>(count);
            while(count-->0) {
                data.Add(reader.ReadString(), reader.ReadInt32());
            }
            return data;
        }
    }

Ответ 6

Безопасно ли использовать BinaryFormatter вместо хранения содержимого непосредственно в текстовом файле? Очевидно нет. Потому что другие могут легко "уничтожить" файл, открыв его блокнотом и добавить что-то, хотя он может видеть только странные символы. Это лучше, если вы храните его в базе данных. Но если вы настаиваете на своем решении, вы можете легко улучшить производительность, используя Parallel Programming в С# 4.0 (вы можете легко получить много полезных примеров, перейдя по ссылке). Что-то выглядит так:

//just an example
Dictionary<string, int> source = GetTheDict();
var grouped = source.GroupBy(x =>
              {
                  if (x.Key.First() >= 'a' && x.Key.First() <= 'z') return "File1";
                  else if (x.Key.First() >= 'A' && x.Key.First() <= 'Z') return "File2";
                  return "File3";
              });
Parallel.ForEach(grouped, g =>
              {
                 ThreeStreamsToWriteToThreeFilesParallelly(g);
              });

Другим альтернативным решением Parallel является создание нескольких потоков, чтение/запись в разные файлы будет выполняться быстрее.