Оператор C ++ iostream ›› работает иначе, чем беззнаковый символ get ()

Я работал над фрагментом кода, чтобы выполнить сжатие, и написал класс битового потока.

Мой класс битового потока отслеживал текущий бит, который мы читаем, и текущий байт (unsigned char).

Я заметил, что чтение следующего беззнакового символа из файла происходило иначе, если я использовал метод >> operator vs get () в классе istream.

Мне просто было любопытно, почему я получаю разные результаты?

ex:

this->m_inputFileStream.open(inputFile, std::ifstream::binary);   
unsigned char currentByte;
this->m_inputFileStream >> currentByte;

vs.

this->m_inputFileStream.open(inputFile, std::ifstream::binary);
unsigned char currentByte;
this->m_inputFileStream.get((char&)currentByte);

Дополнительная информация:

Чтобы быть конкретным, байт, который я читал, был 0x0A, однако при использовании >> он читал бы его как 0x6F

Я не уверен, как они вообще связаны? (они не являются дополнением друг друга?)

Однако оператор >> также определен для работы с символами без знака (см. Справочник по классу c ++ istream


person Setheron    schedule 22.07.2011    source источник
comment
обновил вопрос для получения дополнительной информации   -  person Setheron    schedule 22.07.2011


Ответы (3)


Если вы не разбираете текст, не используйте operator>> или operator<<. Вы получите странные ошибки, которые сложно отследить. Они также устойчивы к модульным тестам, если вы не знаете, что искать. Например, чтение uint8 не удастся, например, на 9.

редактировать:

#include <iostream>
#include <sstream>
#include <cstdint>

void test(char r) {
        std::cout << "testing " << r << std::endl;
        char t = '!';
        std::ostringstream os(std::ios::binary);
        os << r;
        if (!os.good()) std::cout << "os not good" << std::endl;
        std::istringstream is(os.str(), std::ios::binary);
        is >> t;
        if (!is.good()) std::cout << "is not good" << std::endl;
        std::cout << std::hex << (uint16_t)r 
             << " vs " << std::hex << (uint16_t)t << std::endl;
}

int main(int argc, char ** argv) {
        test('z');
        test('\n');
        return 0;
}

производит:

testing z
7a vs 7a
testing 

is not good
a vs 21

Я полагаю, что это никогда не было бы очевидным априори.

person Tom Kerr    schedule 22.07.2011
comment
Ваше первое предложение в порядке. Все остальное - чушь в общем контексте. Если у вас есть какие-то конкретные примеры того, что вы имеете в виду, это было бы конструктивным вкладом, в противном случае я бы счел это мусором и подлежал бы отрицательному голосованию. - person Martin York; 22.07.2011
comment
Я вижу, как это приводит к неправильному значению, но до сих пор не уверен, почему. - person Setheron; 22.07.2011
comment
С точки зрения двоичного кода 0x0A то же самое, что и '\ n'. Однако '\ n' имеет особое значение в текстовом потоке. Операторы сдвига - это (насколько я понимаю) бессмысленная операция над двоичным потоком. Я не понимаю, почему они не терпят неудачу все время. Если вы используете методы чтения / записи на интерфейсах istream / ostream, у вас не будет таких проблем. - person Tom Kerr; 22.07.2011

operator>> предназначен для форматированного ввода. Он прочитает "23" как целое число, если вы передадите его в int, и съест пробелы между токенами. get(), с другой стороны, предназначен для неформатированного побайтного ввода.

person Kerrek SB    schedule 22.07.2011
comment
Правильный ответ. Но OP использует unsigned char, а не int для форматированного ввода. Вы можете объяснить, как это меняет ситуацию. - person Martin York; 22.07.2011
comment
точно. Я использовал беззнаковый символ, поэтому он должен давать мне беззнаковый символ? - person Setheron; 22.07.2011
comment
Вы все еще читаете токен за токеном, поэтому у вас обязательно возникнут проблемы, когда вы получите пробелы или что-нибудь, кроме одного символа, окруженного пробелами. - person Kerrek SB; 22.07.2011

Форматированный ввод C ++ (operator >>) обрабатывает char и unsigned char как символ, а не целое число. Это немного раздражает, но понятно.

Вместо этого вы должны использовать get, который возвращает следующий байт.

Однако, если вы открываете файл с двоичным флагом, вы не должны использовать форматированный ввод-вывод. Вы должны использовать read, write и связанные с ними функции. Форматированный ввод-вывод не будет вести себя правильно, поскольку он предназначен для работы с текстовыми форматами, а не с двоичными форматами.

person Collin Dauphinee    schedule 22.07.2011
comment
Я бы подумал, что обработка char как целого числа будет намного более раздражающей. Как я и ожидал, симметричная операция между ‹< и ››. Итак, если я выведу 0x49, я ожидаю, что ввод будет читать его как 0x49, даже если символ, который он создает, равен 1. std::cout << '1'; char x;std::cin >> x; x лучше быть символом '1' (0x49) - person Martin York; 22.07.2011