2

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?

jpo38
  • 20,821
  • 10
  • 70
  • 151
  • Are you using mingw to make the windows binary by any chance? – user657267 Nov 18 '14 at 01:02
  • No, Visual Studio 2010 SP1 – jpo38 Nov 18 '14 at 07:14
  • Which version of msvcrt is it linking too? – user657267 Nov 18 '14 at 07:15
  • msvcrt.dll: 7.0.7601.17744 – jpo38 Nov 18 '14 at 10:12
  • There may be a bug with that version, at least according to an old [mingw page](http://oldwiki.mingw.org/index.php/Known%20Problems), try linking with something newer, VS 2010 should be bundled with 10.0.something. – user657267 Nov 18 '14 at 10:20
  • 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.... – jpo38 Nov 18 '14 at 10:51
  • 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. – user657267 Nov 18 '14 at 10:52
  • 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`. – jpo38 Nov 18 '14 at 10:57
  • 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. – user657267 Nov 18 '14 at 11:01

1 Answers1

-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;
}
jpo38
  • 20,821
  • 10
  • 70
  • 151