Как правильно использовать std::string для хранения байтов (беззнаковых символов)?

Я кодирую алгоритм сжатия LZ77, и у меня проблемы с сохранением беззнаковых символов в строке. Чтобы сжать любой файл, я использую его двоичное представление, а затем читаю его какchars (поскольку 1 символ равен 1 байту, afaik) до std::string. Все отлично работает сchars. Но через некоторое время я узнал, чтоchar не всегда 1 байт, поэтому я решил заменить его на unsigned char. И здесь начинаются сложности:

  • При сжатии обычного.txt все работает, как ожидалось, я получаю одинаковые файлы до и после распаковки (я предполагаю, что должен, потому что мы в основном работаем с текстом до и после преобразования байтов)
  • Однако при попытке сжать.bmp распакованный файл теряет 3 байта по сравнению с входным файлом (я теряю эти 3 байта при попытке сохранить символы без знака в std::string)

Итак, мой вопрос: есть ли способ правильно сохранить символы без знака в строку?

Я пытался использовать typedef basic_string<unsigned char> ustring и замените все связанные функции их базовыми альтернативами для использования с unsigned char, но я все равно теряю 3 байта.

ОБНОВЛЕНИЕ: я обнаружил, что 3 байта (символа) потеряны не из-за std::string, а из-заstd::istream_iterator (что я использую вместо std::istreambuf_iterator) для создания строки беззнаковых символов (потому что std::istreambuf_iteratorаргумент - символ, а не символ без знака)

Итак, есть ли какие-то решения этой конкретной проблемы?

Пример:

std::vector<char> tempbuf(std::istreambuf_iterator<char>(file), {}); // reads 112782 symbols

std::vector<char> tempbuf(std::istream_iterator<char>(file), {}); // reads 112779 symbols

Образец кода:

void LZ77::readFileUnpacked(std::string& path)

{


std::ifstream file(path, std::ios::in | std::ios::binary);

if (file.is_open())
{
    // Works just fine with char, but loses 3 bytes with unsigned
    std::string tempstring = std::string(std::istreambuf_iterator<char>(file), {});
    file.close();
}
else
    throw std::ios_base::failure("Failed to open the file");
}

2 ответа

char во всех его формах (и std::byte, который изоморфен unsigned char) всегда является наименьшим возможным типом, поддерживаемым системой. Стандарт C++ определяет, чтоsizeof(char) и его вариации всегда должны быть ровно 1.

"Один" что? Это определяется реализацией. Но каждый тип в системе будет несколькоsizeof(char) по размеру.

Поэтому не стоит слишком беспокоиться о системах, в которых charне один байт. Если вы работаете в системе, гдеCHAR_BITSне 8, то эта система вообще не может обрабатывать 8-битные байты напрямую. Такunsigned char не будет отличаться / лучше для этой цели.


Что касается деталей вашей проблемы, istream_iterator принципиально отличается от istreambuf_iteratorитератор. Цель последнего - предоставить итератору доступ к фактическому потоку как к последовательности значений. Цельistream_iterator<T> - разрешить доступ к потоку, как если бы он выполнял повторяющуюся последовательность operator >> звонки с T ценность.

Итак, если вы делаете istream_iterator<char>, то вы говорите, что хотите прочитать поток, как если бы вы stream >> some_char;переменная для каждого доступа к итератору. На самом деле это не изоморфно прямому доступу к символам потока. В частности, FormattedInputFunctions, как operator>> может делать такие вещи, как пропускать пробелы, в зависимости от того, как вы настроили свой поток.

istream_iterator читает используя operator>>которые обычно пропускают пробелы как часть своей функции. Если вы хотите отключить это поведение, вам нужно будет сделать

#include <ios>

file >> std::noskipws;
Другие вопросы по тегам