1
void maintainFileName ()
{
    std :: ifstream myfile;
    myfile.open ("zoomLevels.txt");

    if (myfile.is_open ())
    {
        // Move to end of the file, 
        myfile.seekg (0, std::ios::end);

        // and then six characters back to pick up the last file number.
        myfile.seekg (6, std::ios::beg);

        int len = 1;
        char *t = new char[len];

        myfile.read(t, len);
        qDebug () << "\nt: " << *t << "\n";
    }
    else
    {
        qDebug () << "\nsorry";
    }
}

The file contains this:

78.8115,29.582,1,01.rda
78.8115,29.582,2,02.rda
76.3671,30.2201,1,11.rda
76.3671,30.2201,2,12.rda
78.1908,30.3007,1,01.rda
78.1908,30.3007,2,02.rda
77.3284,29.1415,1,01.rda
77.3284,29.1415,2,02.rda
77.3064,29.1655,1,01.rda
77.3064,29.1655,2,02.rda

The value returned by that function is 5, whereas the sixth character from the end is 0!
Where am I going wrong?

Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411
  • You got the meaning of the `dir` parameter wrong. It's not a direction ! You are probably not the first one to get caught though: it's utterly missleading: it's actually a position ! – J.N. Sep 05 '12 at 07:55
  • @J.N. See this: http://www.cplusplus.com/reference/iostream/istream/seekg/ Is it written there: `Seeking direction`. :) – Aquarius_Girl Sep 05 '12 at 08:00
  • Quite from the link: "**Seeking direction.** It is an object of type ios_base::seekdir that specifies an **absolute position** from where the offset parameter off is applied." Direction/absolute position. No wonder people get confused. – jrok Sep 05 '12 at 08:02
  • 1
    The following link is much clearer though: http://en.cppreference.com/w/cpp/io/basic_istream/seekg. Still has the `dir` name, probably coming from the standard. – J.N. Sep 05 '12 at 08:08

5 Answers5

7

Seeking to an arbitrary position in a text file is undefined behavior. In practice, it will probably work under various Unices, but no where else. If you open the file in binary mode, the seek is legal. Formally, if you open the file in binary mode, you may get extra nul bytes at the end, but in practice, this isn't a problem today. If you open it in binary mode, however, you may see something else instead of '\n' in the data; under Windows, for example, you'll see the two character sequence 0x0D, 0x0A.

Of course, in your code, you're seeking from the beginning, not from the end. This is also undefined behavior, but most of the time, it will work as long as you are seeking in the first line.

And finally, the sixth character from the end in the data you show is a '2', not a '0', as you write. But of course, on systems other than Unix, you could easily see something else (or get an error): probably a '.' under Windows, or or an error (or maybe a '' ') under some mainframe OS.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • You were so correct. I am on Linux. Now I opened the file in binary mode, and the correct result was shown. Of course, the first character at the end would be EOF, so my calculation was wrong. Thanks for the help. – Aquarius_Girl Sep 05 '12 at 08:08
  • @AnishaKaul: There is no "EOF character". This must be one of the most pervasive and ineradicable misconception among C programmers. – Kerrek SB Sep 05 '12 at 08:09
  • But when I wrote `myfile.seekg (-7, std::ios::end);` it did show `0`. But, if you actually count the values, `0` is in the sixth position from the end! – Aquarius_Girl Sep 05 '12 at 08:12
  • There might be a `'\n'` at the end. – jrok Sep 05 '12 at 08:12
  • @jrok AHA, absolutely, there IS. :) – Aquarius_Girl Sep 05 '12 at 08:14
  • 1
    @AnishaKaul If you're on Linux, this type of positioning actually works, even in text mode (at least with the library which comes with g++). In many ways, "text mode" is designed to mean "what Unix does". But as I said, you miscounted. There is no EOF character in the file, but every line ends with a `'\n'`, which is present in the file. (In binary mode, under Windows, you'ld have to count two bytes for the end of line. On a mainframe, you might not be able to seek relative to the end at all. This sort of variation is why the standard makes it undefined behavior.) – James Kanze Sep 05 '12 at 08:24
  • Reading through the stream-related sections of the Standard I couldn't actually find any statement that limits absolute-position seeking to binary streams. Did I overlook anything, or is it that the Standard actually defines this properly, but on some platforms it's just too difficult to implement this correctly? – jogojapan Sep 05 '12 at 08:34
  • For `seekpos` (§27.9.1.5/17 in C++11), "If sp has not been obtained by a previous successful call to one of the positioning functions (seekoff or seekpos) on the same file the effects are undefined.". For `seekoff`, C++ forwards to `fseek` in the C standard, which has similar constraints (although I don't have a copy here to quote); all that's legal on a stream opened in text mode is `0` or a value returned from `ftell`, and `whence` must be `SEEK_SET`. – James Kanze Sep 05 '12 at 09:16
4
myfile.seekg (6, std::ios::beg);

here you are moving 6 characters from the begining, not towards the begining. Just use

myfile.seekg (-6, std::ios::end);
SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105
2

You can try determining the complete size of the file via tellg() at the end of the file and the subtract your numbers, validate it against > 0 and then seek to it again. If you try this, you should also ensure the file is opened in binary mode (I remember, there may be a flaw)

myfile.seekg (0, ios::end);
// You have to ensure, myfile.tellg() > 6
myfile.seekg ( myfile.tellg() - 6, ios::beg );

Edit:

seekg takes the type std::streamoff as the offset.

The Standard (ISO/IEC 14882:2003) says some very interesting things about this "problem" here many people discuss about.

In section 27.2. Forward declarations, the streampos is of class fpos.

If we went further, we can find the requirements table for fpos at section 27.4.3.2, where we can get the closure to the streamoff type, and here an explicit requirement is: q = p + o, so the fpos class MUST DEFINE an operator+( offset). Since the fpos object has also to define a O(p) with return type OFF_T which is a internal type, but there is also a statement, the std::streamoff is of type OFF_T we have a closed loop to a definition inside the standard for this operation.

So this operation should be well defined.

Other opinions are welcome.

Sergei Krivonos
  • 4,217
  • 3
  • 39
  • 54
Sven
  • 710
  • 9
  • 18
  • 2
    Undefined behavior. And the standard is very ambiguous with regards to what adding or subtracting a value to a `streampos` might mean. (The standard actually places impossible requirements on the behavior of `streampos`.) – James Kanze Sep 05 '12 at 08:28
  • I have edited, why this is well defined inside the standard, other opinions are welcome to discuss. – Sven Sep 05 '12 at 12:25
  • The standard says explicitly seeking on a text file, other than to a position returned by `tell`, is undefined behavior. You don't need to reason about it. And of course, in practice, arithmetic on a `streampos` doesn't work on files opened in text mode, except under Unix. – James Kanze Sep 05 '12 at 12:43
  • Can you please add a reference here? And if you have read the post above, I included the restriction on binary opened files. – Sven Sep 05 '12 at 12:56
  • Aha, you changed the problem, then. The OP's file was opened in text mode. (But I'm still not too sure what adding or subtracting an `int` to a `streampos` can mean. A `streampos` contains state information, as well as the position. Think of what it might mean on a wide character stream imbued with a UTF-8 locale, for example. – James Kanze Sep 05 '12 at 13:15
1

The first seek skips to the end and the second one skips to the beginning + 6.

Use:

 myfile.seekg(-6, std::ios::end);
rasmus
  • 3,136
  • 17
  • 22
0

First, go to the end of the file: is.seekg (0, ios::end); , then save the position: file_end_position = is.tellg(); . Now jump to that position: is.seekg (file_end_position-6, ios::end); , and read the 6th character from the end: is.read (buf,1);

#include <iostream>
#include <fstream>
using namespace std;

int main () {
  int file_end_position;

  ifstream is;
  is.open ("test.txt", ios::binary );

  // get size of file:
  is.seekg (0, ios::end);
  file_end_position = is.tellg();
  is.seekg (0, ios::beg);


  //go to the end  of the file -6
  is.seekg (file_end_position-6, ios::end);

  char buf[1];

  // read the 6th character from the end of the file  
  is.read (buf,1);
  is.close();



  delete[] buffer;
  return 0;
}
Software_Designer
  • 8,490
  • 3
  • 24
  • 28