Как правильно использовать 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;