C++ читает неподписанный символ из файлового потока

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

#include <iostream>
#include <fstream>
#include <vector>
#include <istream>

std::string filename("file");
size_t bytesAvailable = 128;
size_t toRead = 128;

std::basic_ifstream<unsigned char> inf(filename.c_str(), std::ios_base::in | std::ios_base::binary) ;
if (inF.good())
{
    std::vector<unsigned char> mDataBuffer;
    mDataBuffer.resize(bytesAvailable) ;
    inF.read(&mDataBuffer[0], toRead) ;
    size_t counted = inF.gcount() ;
}

Это приводит к чтению всегда в 0 байтов, как показано переменной подсчитано.

Кажется, в Интернете есть ссылки, говорящие о том, что мне нужно установить локаль, чтобы эта работа работала. Как это сделать точно мне не понятно.

Тот же код работает с использованием типа данных "char" вместо "unsigned char"

Приведенный выше код с использованием unsigned char, похоже, работает в Windows, но не работает в colinux Fedora 2.6.22.18 .

Что мне нужно сделать, чтобы заставить его работать на Linux?

4 ответа

Решение

C++ требует реализации только для предоставления явных специализаций для двух версий черт характера:

std::char_traits<char>
std::char_traits<wchar_t>

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

Если вы создаете экземпляр потока, как

std::basic_ifstream<unsigned char>

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

ошибка: специализация std::char_traits не может быть создана.

я хотел бы использовать ifstream вместо этого (который является basic_ifstream<char>) а затем пойти и прочитать в vector<char>, При интерпретации данных в векторе вы все равно можете преобразовать их в unsigned char потом.

Не используйте basic_ifstream, так как он требует специализации.

Использование статического буфера:

linux ~ $ cat test_read.cpp
#include <fstream>
#include <iostream>
#include <vector>
#include <string>


using namespace std;

int main( void )
{
        string filename("file");
        size_t bytesAvailable = 128;

        ifstream inf( filename.c_str() );
        if( inf )
        {
                unsigned char mDataBuffer[ bytesAvailable ];
                inf.read( (char*)( &mDataBuffer[0] ), bytesAvailable ) ;
                size_t counted = inf.gcount();
                cout << counted << endl;
        }

        return 0;
}
linux ~ $ g++ test_read.cpp
linux ~ $ echo "123456" > file
linux ~ $ ./a.out
7

используя вектор:

linux ~ $ cat test_read.cpp

#include <fstream>
#include <iostream>
#include <vector>
#include <string>


using namespace std;

int main( void )
{
        string filename("file");
        size_t bytesAvailable = 128;
        size_t toRead = 128;

        ifstream inf( filename.c_str() );
        if( inf )
        {

                vector<unsigned char> mDataBuffer;
                mDataBuffer.resize( bytesAvailable ) ;

                inf.read( (char*)( &mDataBuffer[0]), toRead ) ;
                size_t counted = inf.gcount();
                cout << counted << " size=" << mDataBuffer.size() << endl;
                mDataBuffer.resize( counted ) ;
                cout << counted << " size=" << mDataBuffer.size() << endl;

        }

        return 0;
}
linux ~ $ g++ test_read.cpp -Wall -o test_read
linux ~ $ ./test_read
7 size=128
7 size=7

используя резерв вместо изменения размера при первом вызове:

linux ~ $ cat test_read.cpp

#include <fstream>
#include <iostream>
#include <vector>
#include <string>


using namespace std;

int main( void )
{
        string filename("file");
        size_t bytesAvailable = 128;
        size_t toRead = 128;

        ifstream inf( filename.c_str() );
        if( inf )
        {

                vector<unsigned char> mDataBuffer;
                mDataBuffer.reserve( bytesAvailable ) ;

                inf.read( (char*)( &mDataBuffer[0]), toRead ) ;
                size_t counted = inf.gcount();
                cout << counted << " size=" << mDataBuffer.size() << endl;
                mDataBuffer.resize( counted ) ;
                cout << counted << " size=" << mDataBuffer.size() << endl;

        }

        return 0;
}
linux ~ $ g++ test_read.cpp -Wall -o test_read
linux ~ $ ./test_read
7 size=0
7 size=7

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

Если вы работаете в Windows, вы можете напрямую использовать:

      using ufstream = std::basic_fstream<unsigned char, std::char_traits<unsigned char>>;
ufstream file;

В Linux нет такой удачи, поскольку фасеты или локали unsigned_char не предоставляются, поэтому следуйте подходу @Johannes.

Гораздо проще:

#include <fstream>
#include <vector>

using namespace std;


int main()
{
    vector<unsigned char> bytes;
    ifstream file1("main1.cpp", ios_base::in | ios_base::binary);
    unsigned char ch = file1.get();
    while (file1.good())
    {
        bytes.push_back(ch);
        ch = file1.get();
    }
    size_t size = bytes.size();
    return 0;
}
Другие вопросы по тегам