Home > OS >  tellg returning negative value after using getline and seekg
tellg returning negative value after using getline and seekg

Time:02-04

I am needing to parse through a text file and output the vowels within this textfile line by line into a separate file. This all needs to be done using one filestream at a time.

I open the input stream using fstream ios:: in & ios::binary, read in one line using getline, clear & close that stream then open my output fstream using ios::out & ios::app, find the vowels and output them one by one as they are found. Then close output stream and open the input stream. Done using seekg to put the file at the end of the prior line in hopes that getline will read in the consecutive line.

fstream a(fileName, ios::in | ios::binary | ios::ate);
    string OF = "vowels_"   fileName;
    while (count < size && count != -1) {
        count = a.tellg();
        a.close();
        fstream b(OF, ios::out| ios::app);
        for (int i = 0; i < line.length(); i  ) {
            if (line.at(i) == '\n'){b << '\n';}
            if (line.at(i) == 'a' || line.at(i) == 'e' || line.at(i) == 'i' || line.at(i) == 'o' || line.at(i) == 'u') {
                b << line.at(i); vowels = line.at(i);
            }
        }
        if(vowels.empty()){b << endl;}
        if (line.empty()){b << endl;}
        b.close();
        fstream a(fileName, ios::in | ios::binary | ios::ate);
    }

CodePudding user response:

fstream a(fileName, ios::in | ios::binary | ios::ate); at the end of the loop does not re-initialize a, it creates a new fstream.
When you get to the start of the next iteration, a is closed.

You are overcomplicating this.
Work line by line in text mode, keep track of how many lines you've read, and skip over that many lines on each iteration.

Something like this:

size_t skip_lines = 0;
while (true)
{
    std::string line;
    {
        ifstream in(fileName);
        size_t lines = 0;
        while (lines < skip_lines && std::getline(in, line))
        {
            lines  ;
        }
        if (!std::getline(in, line))
        {
            break;
        }
    }
    skip_lines  = 1;
    ofstream out(OF, ios::append);
    for (auto it: line)
    {
        if (is_vowel(it)) // Define 'is_vowel' elsewhere.
        {
            out << it;
        }
    }
    out << '\n';
}

CodePudding user response:

Normally, you would use a different approach. You would simply open both input and output files and then do the task in one shot.

But, obviously, the task is, to have only one stream open at the same time. And maybe, it should teach you how to use tellg and seekg functions.

And one of the most important things that you need to do: Please always check the return value of each function call. For read functions, this is an absolute must.

Then, handle potential errors, or states.

You can and should also use those return values for the control logic in your program.

What you should additionally know is, that, if stream variables go out of scope, they will automatically close the associated file. Please make use of this property.

Then, the solution: We need to set up a variable with the current file position. At the beginning, this is 0. Then we read data and, store the new file position after reading. We can close now the source file, because, we know the position of the next characters to read. So, close the file, and do the “vowel”-work. Simply check, if a character is a vowel, and then write it to the destination file.

Do this, until we get any kind of error of “EOF” (end of file).

A potential solution is shown below. I put many comments in the file for easier understanding.

Please see:

#include <iostream>
#include <fstream>
#include <string>

const std::string sourceFileName{ "test.txt" };

int main() {

    // Define the filename for the destination file
    std::string destinationFileName = "vowel_"   sourceFileName;

    // Here we will store the current file position
    std::ifstream::pos_type readPosition{};

    // We will use a boolean variable to indicate, how long we want to read
    bool continueToRead{ true };

    // Read the source file in a while loop
    while (continueToRead) {

        // Open the source file for reading
        std::ifstream sourceFileStream{ sourceFileName };

        // Check, if the file could be opened. 
        // This calls the "bool" function of the stream, to check, if everything is OK
        if (sourceFileStream) {

            // Source file could be opened. Set the file read pointer to the last known position
            if (not sourceFileStream.seekg(readPosition)) {
                // If thís did not work (end of file or something)
                continueToRead = false;
            }
            else {
                // Read one line and check, if that worked.
                std::string line{};
                if (std::getline(sourceFileStream, line)) {

                    // Now we need to store the current file position for the next loop run
                    readPosition = sourceFileStream.tellg();

                    // Because, we want to have only one file stream open at a time,
                    // we will now close the input file stream
                    sourceFileStream.close();

                    // We may need to write to the output file. Open this now and check, 
                    // if it could be opened. Set file pointer to end with "app"
                    std::ofstream destinationFileStream(destinationFileName, std::ios::app);

                    // Now iterate over the source line string and write vowels, if necessary
                    for (const char c : line) {
                        // check, if c is a vowel
                        if ((c == 'a') or (c == 'e') or (c == 'i') or (c == 'o') or (c == 'u')
                            or (c == 'A') or (c == 'E') or (c == 'I') or (c == 'O') or (c == 'U')) {

                            // Write vowel to destination file
                            destinationFileStream << c;
                        }
                    }
                    // Write final newline character
                    destinationFileStream << std::endl;

                    // Here the destinationFileStream will go out of scope and closed automatically
                }
                else {
                    // Could not read data. Stop loop
                    continueToRead = false;
                }
            }
        }
        else {
            // Source file could not be opened.
            std::cout << "\nError: Could not open source file '" << sourceFileName << "'\n\n";
            continueToRead = false;
        }
    }
}
  • Related