Использование fstream::seekg под windows для файла, созданного под Unix

У меня есть C++ кроссплатформенная программа (скомпилирована с g++ под Linux и с Visual Studio под ПК). Эта программа записывает строки в текстовый файл (используя << оператор и std::endl), но также может считывать данные обратно из сгенерированного текстового файла (используя std::getline).

Чтобы оптимизировать доступ к данным и сохранить память, при чтении файла данных я впервые читаю его и сохраняю положение данных в моей программе. Когда нужны данные, я позже использую seekg перейти к определенной позиции и прочитать данные.

  • Создание и чтение файла на ПК работает нормально.
  • Создание и чтение файла в Linux работает нормально.
  • Но создание файла в Linux и чтение на ПК не удается.

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

#include <fstream>
#include <iostream>
#include <string>
#include <assert.h>
int main()
{
    std::fstream file;
    file.open( "buglines.txt", std::ios_base::in );
    if ( file.is_open() )
    {
        std::streampos posLine2;
        std::string lineStr;
        std::string line2Str;
        int line = 1;
        while ( std::getline( file, lineStr ) )
        {
            if ( line == 1 )
                posLine2 = file.tellg(); // save line 2 position
            if ( line == 2 )
                line2Str = lineStr; // save line 2 content

            ++line;
            std::cout << lineStr <<std::endl;
        }
        std::cout << "Reached EOF, trying to read line 2 a second time" << std::endl;
        file.clear(); // clear EOF flag
        file.seekg(posLine2); // move to line 2
        std::getline( file, lineStr ); // read the line
        assert( lineStr == line2Str ); // compare

    }
    return 0;
}

Я запускаю это из Windows.

  • Если buglines.txt был создан под Windows (шестнадцатеричный редактор показывает разделители строк как 2 символа 0x0D 0x0A), оно работает (lineStr == line2Str).
  • Если buglines.txt был создан под Linux (шестнадцатеричный редактор показывает разделители строк как 1 символ 0x0A), это не работает (lineStr - пустая строка). Даже если цикл getline работал отлично.

Я знаю, что обе системы работают по-разному с EOL, но, поскольку я просто использую getline функция для чтения, я надеялся, что это будет работать умно... я что-то упустил?

1 ответ

Решение

Я не могу легко обновить библиотеку времени выполнения для своего проекта, и, как видно, другого "решения" нет.

Я пытался установить std::ios_base::binary атрибут при открытии файла. Это решает сообщенную проблему, но вводит новую: мы получаем дополнительную \r главы при чтении файла с getline,

Так что, если у кого-то есть такая же проблема и нужно ее исправить, вот обходной путь: просто закройте файл, снова откройте его, а затем сгорите первые n символов, чтобы переместить указатель чтения в хорошее место:

#include <fstream>
#include <iostream>
#include <string>
#include <assert.h>

int main()
{
    std::fstream file;
    const std::string fileName = "buglines.txt";
    file.open( fileName.c_str(), std::ios_base::in );
    if ( file.is_open() )
    {
        std::streampos posLine2;
        std::string lineStr;
        std::string line2Str;
        int line = 1;
        while ( std::getline( file, lineStr ) )
        {
            if ( line == 1 )
                posLine2 = file.tellg(); // save line 2 position
            if ( line == 2 )
                line2Str = lineStr; // save line 2 content

            ++line;
            std::cout << lineStr << std::endl;
        }
        std::cout << "Reached EOF, trying to read line 2 a second time" << std::endl;
        //file.clear(); // clear EOF flag
        //file.seekg(posLine2); // move to line 2
        file.close();
        file.open( fileName.c_str(), std::ios_base::in );
        assert( file.is_open() );
        char* temp = new char[static_cast<int>(posLine2)+1];
        file.read( temp, static_cast<int>(posLine2)+1 ); // if posLine2 is too big, consider splitting with in a loop
        delete [] temp;
        assert( file.tellg() == posLine2 );

        std::getline( file, lineStr ); // read the line
        assert( lineStr == line2Str ); // compare
    }
    return 0;
}
Другие вопросы по тегам