Wiered Behavious of of seekg() and getc()

In my following code I'm trying to read a file named "forward.txt" and output on console "character by character" all the characters in reverse order but what happens is just last line is output in reverse successfully but after that program keeps on putting newlines on console but doesn't start reading the 2nd last line(which is first line as file has only two lines in it)

I'm just a beginner so please tell me the details, I wanna learn it in detail
I'm studying in First Semester in Software Engineering

following is the text which is in the file
This is a file wirtten in forward stance and my cpp program will reverse all the characters in this file.
So this is not gonna be tricky right!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
1. Comments saying console are just user console outputs so no worries
with them while debugging or reading simple logic of program

2. Variables are indicated in double quotes "variable_name"
*/

//headers----------------
#include <iostream>
#include <stdlib.h>
#include <fstream>
//----------------headers

using namespace std;

main(){
       
//variables---------
fstream ff;                          //file handle
//-------variables

//initializations-----------
//----------initializations
              
//main-code-------------

ff.open("forward.txt", ios::out | ios::in);                //opened forward.txt for read/write

if(!ff){                                                   //error-check
cout << "Failed" << endl;
}else{
      
      char c = 0;                                          //read buffer
      
      ff.seekg(-1L, ios::end);                             //position cursor to character before the last one in file
      ff.get(c);                                           //read in "c"
      cout << c;                                           //console "c"
      
      for(int i = 0; i < 50; i++){

              ff.seekg(-2L, ios::cur);                     //position cursor 2 characters back
//cursor if reads at 30 after reading it would be at 31 taking two positions back will be 29
//so this way after reading 30th character next character read by this logic will be 29 then
// continuing 28, 27, 26 and so on till loop ends

              ff.get(c);                                  //read in "c"
              cout << c;                                  //console "c"
              
              }//-----for

ff.close();                                                //close the opened file
}//---------else

//-----------main-code

//system-pause--------
{
cout << endl;
system("pause");
}//------system-pause
       
       }//---------main() 
The problem is that on windows the end of line are two characters: \r\n

seekg operates on the the real number of characters while get(c) converts \r\n int one character \n but advances the real two characters. So if you go back two characters you're again at the end of line.

This happens in text mode. To solve this switch to binary and don't cout \r.

Read this:
http://www.cplusplus.com/reference/fstream/fstream/fstream/
You shouldn't seek to locations in files opened in text mode which you haven't previously retrieved with a tell.

IIRC, the standard doesn't require relative seeks to work on text files (which is the only type of seek you were using.)

as coder777 said I used and if statement to check for \r and \n character if found I decremented seek location two more bytes back but still it's stuck on newline loop

cire as I said I'm beginner so I wanna know what is the problem with seek(just for knowledge purpose) and what is the correct logic to use if I want to reverse a file contents?
cire as I said I'm beginner so I wanna know what is the problem with seek(just for knowledge purpose)

I believe you've already stumbled across it.

what is the correct logic to use if I want to reverse a file contents?

In this case, reading the cotents in and then reversing them in memory is probably good enough.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <deque>
#include <algorithm>

int main()
{
    // our makeshift file contents.  Replace with std::ifstream in("filename")
    std::istringstream in(
        "Jack and Jill went up the hill\n"
        "To fetch a pail of water\n"
        "Jack fell down and broke his crown\n"
        "And Jill came tumbling after\n"
    );

    std::deque<std::string> lines;      

    {
        std::string line;
        while (std::getline(in, line))                  // get a line.
        {
            std::reverse(line.begin(), line.end());     // reverse it.
            lines.push_front(line);                     // store it in our container.
        }
    }

    for (auto& line : lines)                     
        std::cout << line << '\n';
}


http://ideone.com/y0g2as
things in your code are new to me I just know about fstream, handle.get() {character at a time} and handle.getline() {line at a time} and handle.read() {many bytes at a time}. Actually if I couldn't do this task using what is covered in my course content then I guess I'm not gonna get points regardless how efficient code I may have written.

and actually the truth my task is to use only handle.get() as my teacher told me to take and put a character at a time from file. just give me a way around that \r\n thing and I'll be done.

just wanna jump across newline without being trapped by newline a.k.a \r\n for windows this is giving me tough time.
You could use a simple recursive solution to avoid the container and the hackish backwards reading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <fstream>
#include <sstream>
#include <iostream>

void print_backwards(std::istream& in)
{
    char ch;
    if (!in.get(ch))                        // base condition: Nothing more to be extracted from stream.
        return;

    print_backwards(in);                    // extract more input and print it prior to
    std::cout << ch;                        // printing this character.
}

int main()
{
    // our makeshift file contents.  Replace with std::ifstream in("filename")
    std::istringstream in(
        "Jack and Jill went up the hill\n"
        "To fetch a pail of water\n"
        "Jack fell down and broke his crown\n"
        "And Jill came tumbling after\n"
    );

    print_backwards(in);
}


http://ideone.com/qA0G32


Alternatively, just read it all in as one string and print out the string in reverse.

Or, if you insist on the backwards seeking, know that the newline takes 2 characters. So if you do a relative seek of -2 from the current position, you're at the beginning of the newline character. When you read that character, the position jumps two. So if you then do a relative seek of -2 from the current position, you're at the beginning of the newline character... That's headed for an infinite loop.

The solution in that case would be to do a relative seek of -3 from the current position so that you're now at the character prior to the newline. Of course, if you have multiple newlines in a row, that complicates things. Like I said, this isn't something you should count on working.
Last edited on
Well this code worked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*
1. Comments saying console are just user console outputs so no worries
with them while debugging or reading simple logic of program

2. Variables are indicated in double quotes "variable_name"
*/

//headers----------------
#include <iostream>
#include <stdlib.h>
#include <fstream>
//----------------headers

using namespace std;

main(){
       
//variables---------
fstream ff;                          //file handle
char buffer[500];                    //buffer
//-------variables

//initializations----------
for(int i = 0; i < 500; i++){
      
      buffer[i] = '\0';
      
      }//-----for
//----------initializations
              
//main-code-------------

ff.open("reversefile.txt", ios::out | ios::in);            //opened reversefile.txt for read/write

if(!ff){                                                   //error-check
cout << "Failed" << endl;
}else{
      
      ff.read(buffer, 500);
      
ff.close();                                                //close the opened file
}//---------else

for(int i = strlen(buffer); i >= 0; i--){                  //for generating "i" in reverse
      
      cout  << buffer[i];                                  //console reverse characters as "i" is reverse now
      
      }//------for

//-----------main-code

//system-pause--------
{
cout << endl;
system("pause");
}//------system-pause
       
       }//---------main() 


but the character by character method is too messy I guess that is either not possible or too hot for me to touch it right now, but one day I'm gonna get the logic behind that method too
Last edited on
Topic archived. No new replies allowed.