Почему basic_ifstream показывает мне неправильные результаты? - PullRequest
2 голосов
/ 02 июня 2011

У меня есть бинарный файл. В верхней половине сечения хранится 2288 * 2288 значений плавания по долготе, и такое же количество значений плавания по широте занимало нижнюю половину. Я использовал следующий код, чтобы загрузить их в вектор с плавающей точкой. Он может работать как шарм, но дал мне неверные результаты. Что касается моего двоичного файла, вектор с плавающей точкой должен быть заполнен в общей сложности 2288 *2288* 2 = 10469888 элементов, но только 159005, все их значения одинаковы 200.0000. Не могли бы вы объяснить, что не так с моим кодом?

Заранее спасибо!

bool LoadData(const char* pszDataFile)
{   
    typedef char_traits<float> traits_type;
    typedef std::codecvt<float, char, mbstate_t> cvt;

    std::basic_ifstream<float, traits_type> input( pszDataFile, std::ios::binary );
    std::locale loc(std::locale(), new cvt());
    input.imbue(loc);

    std::vector<float> fvBuffer;    

    // Copies all data into buffer 
    std::copy(std::istreambuf_iterator<float>(input),          
              std::istreambuf_iterator<float>( ),         
              std::back_inserter(fvBuffer)); 

    long nSzie = fvBuffer.size();  // Wrong vector size (159005)

    return true;
}

Ответы [ 2 ]

1 голос
/ 02 июня 2011

Если вы заполняете поток файлов () после открытия файла, функция imbue () автоматически завершается с ошибкой.

Вы должны выполнить imbue (), а затем открыть файл:

std::basic_ifstream<float, traits_type> input;
std::locale loc(std::locale(), new cvt());
input.imbue(loc);

// Open after the imbue()
input.open( pszDataFile, std::ios::binary );

Ваша вторая проблема - вы путаете двоичный файл терминов:

Этот код считывает поток текстовых данных (то есть текстовый файл), используя оператор >>

std::copy(std::istreambuf_iterator<float>(input),          
          std::istreambuf_iterator<float>( ),         
          std::back_inserter(fvBuffer)); 

Использование std::ios::binary при открытии файла влияет только на то, как генерируется «последовательность конца строки» (EOLS), оно не имеет никакого отношения к природе файла. Хотя, поскольку вы указываете это, символ '\ n' не преобразуется в EOLS, что полезно при создании двоичных файлов.

Так что короткий ответ - лучше читать текстовый поток из файла.

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

Кроме того, я не могу заставить ваш код компилироваться.
Так что вы делаете что-то еще нестандартное.

0 голосов
/ 02 июня 2011

Чтобы заставить работать std :: basic_ifstream, вы должны определить свой trait_type так, чтобы он предоставил все ожидаемое входным потоком, и я не уверен, что это будет возможно. Это будет гораздо больше, чем просто codecvt<float, char, mbstate_t> (который, как вам кажется, уже присутствует, в то время как стандарт требует только специализаций wchar_t, char и char, char).

Если вам нужен итератор двоичного ввода, вам придется написать его самостоятельно для работы с basic_ifstream, что-то вроде этого (запустить и дать ожидаемый результат, но не отлаживать дальше):

#include <fstream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>

template <typename T>
class BinaryInputIterator
    : public std::iterator<std::input_iterator_tag, T, std::ptrdiff_t,
                           const T*, const T&>
{
public:
    BinaryInputIterator();
    BinaryInputIterator(std::istream&);
    // Compiler generated version OK:
    // BinaryInputIterator(BinaryInputIterator const& other);
    // BinaryInputIterator& operator=(BinaryInputIterator const& other);
    ~BinaryInputIterator();

    T const& operator*() const;
    T const* operator->() const;
    BinaryInputIterator& operator++();
    BinaryInputIterator operator++(int);

private:
    std::istream* myStream;
    T myValue;

    friend bool operator==
       (BinaryInputIterator const& l, BinaryInputIterator const& r)
    {
        return ((l.myStream == NULL && (r.myStream == NULL || !*r.myStream))
                || (r.myStream == NULL && (l.myStream == NULL || !*l.myStream)));
    }
    friend bool operator!=
       (BinaryInputIterator const& l, BinaryInputIterator const& r)
    {
        return !(l == r);
    }
};

template <typename T>
BinaryInputIterator<T>::BinaryInputIterator()
    : myStream(0)
{}

template <typename T>
BinaryInputIterator<T>::BinaryInputIterator(std::istream& is)
    : myStream(&is)
{
    myStream->read(reinterpret_cast<char*>(&myValue), sizeof myValue);
}

template <typename T>
BinaryInputIterator<T>::~BinaryInputIterator()
{}

template <typename T>
T const& BinaryInputIterator<T>::operator*() const
{
    return myValue;
}

template <typename T>
T const* BinaryInputIterator<T>::operator->() const
{
    return &myValue;
}

template <typename T>
BinaryInputIterator<T>& BinaryInputIterator<T>::operator++()
{
    myStream->read(reinterpret_cast<char*>(&myValue), sizeof myValue);
    return *this;
}

template <typename T>
BinaryInputIterator<T> BinaryInputIterator<T>::operator++(int)
{
    BinaryInputIterator result(this);
    ++*this;
    return result;
}

int main()
{
 {
    std::ofstream os("foo.dta");
    std::vector<float> vect1;
    vect1.push_back(4.2);
    vect1.push_back(3.14);
    os.write(reinterpret_cast<char*>(&vect1[0]), sizeof(float)*vect1.size());
 }
 {  
    std::ifstream is("foo.dta");
    std::vector<float> vect2;
    std::copy(BinaryInputIterator<float>(is),
              BinaryInputIterator<float>(),
              std::back_inserter(vect2));
    std::copy(vect2.begin(), vect2.end(), std::ostream_iterator<float>(std::cout, "\n"));
 }
}
...