Using fstream::seekg under windows on a file created under Unix
Asked Answered
S

1

3

I have C++ a cross-platform program (compiled with g++ under Linux and with Visual Studio under PC). This program writes lines to a text file (using << operator and std::endl) but can also read data back from the generated text file (using std::getline).

To optimize data access and save memory, when reading the data file, I read it a first time and save data position in my program. When data is needed, I later use seekg to move to a specific position and read the data.

  • Creating and reading the file on PC works fine.
  • Creating and reading the file on Linux works fine.
  • But creating the file on Linux and reading on PC fails.

Under PC, seekg sometime fails to move the cursor accordingly. I could isolate the problem in the example below. It reads the file once, saves second lineposition and value, then moves back to the saved position and reads the line again.

#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;
}

I'm running this from Windows.

  • If buglines.txt was created under Windows (hexadecimal editor shows line separators as 2 characters 0x0D 0x0A), it works (lineStr == line2Str).
  • If buglines.txt was created under Linux (hexadecimal editor shows line separators as 1 character 0x0A), it does not works (lineStr is empty string). Even if the getline loop worked perfectly.

I know both system deals differently with EOL, but as I'm just using getline function for reading, I was hoping that it would smartly work...am I missing something?

Stricklin answered 17/11, 2014 at 12:59 Comment(9)
Are you using mingw to make the windows binary by any chance?Trimester
No, Visual Studio 2010 SP1Stricklin
Which version of msvcrt is it linking too?Trimester
msvcrt.dll: 7.0.7601.17744Stricklin
There may be a bug with that version, at least according to an old mingw page, try linking with something newer, VS 2010 should be bundled with 10.0.something.Trimester
I'm using Visual Studio 10.0.40219.1 SP1Rel and msvcrt.dll is picked up from C:\Windows\SysWOW64. I checked my Visual installation folder + the whole Windows folder and there is no newer version anywhere. There is no Visual Studio SP2 available...do you mean I should migrate to Visual Studio 2012 for instance? I'm not very confident in downloading a msvcrt.dll file and replacing Windows's one....Stricklin
No need to replace, I believe there is a switch you can enable in the app config in VS to link against a newer dll, it should be msvcrt100.dll. Sorry not too familiar with VS.Trimester
There is no such file. There is msvcr100.dll (with no t) and msvcp100.dll but no msvcrt100.dll, this one's name is apparently not versioned, I only find msvcrt.dll.Stricklin
sorry that's what I meant, are you linking to the runtime statically? Apparently this will add msvcrt.dll as a dependency, try dynamic linkage instead.Trimester
S
-1

I can't easily upgrade the runtime library for my project and as, apparently, there is no other "solution".

I tried to set std::ios_base::binary attribute upon file open. It fixes the reported problem but introduces a new one: We get extra \r chacters when reading the file with getline.

So if anyone has the same problem and needs a fix, here is a workaround: simply close the file, re-open it, and then eat the first n characters to move the read pointer to the good location:

#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;
}
Stricklin answered 20/11, 2014 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.