Kim Chan
Kim Chan

Reputation: 3

Reading a file and ignoring whitespace

how can you able to convert video.movieTitle, video.movieGenre and video.movieProduction into a string. I test my program but it only reads the line and ignore whitespace.

while((infile,"%d\n",&p->video.videoID),
    fscanf(infile,"%s\n",p->video.movieTitle.c_str()),
    fscanf(infile,"%s\n",p->video.movieGenre.c_str()),
    fscanf(infile,"%s\n",p->video.movieProduction.c_str()),
    fscanf(infile,"%d\n",&p->video.numberOfCopies))

my file contains:

101

Raya and the Last Dragon

Animation

Walt Disney Studio

6

102

Captain America: The First Avenger

Adventure 

Marvel

1

Upvotes: 0

Views: 86

Answers (2)

Lasersköld
Lasersköld

Reputation: 2225

Like this:

Setup and test input


#include <sstream>
#include <iostream>

using namespace std; // For readability on so

// Your example input
const char* testString = R"_(

101

Raya and the Last Dragon

Animation

Walt Disney Studio

6

102

Captain America: The First Avenger

Adventure 

Marvel

1
)_";

How to read a single non empty line


namespace {

// Read a single non empty line
// return "" if file is not good anymore
std::string line(std::istream &stream) {
    if (stream) {
        string s;
        while (stream && s.empty()) { // Skip empty lines
            getline(stream, s);
        }
        return s;
    }

    return {};
}

} // namespace

Then use it in your function like this


int main(int, char**) {
    auto file = istringstream{testString}; // example
    // auto file = ifstream{filename} // real implementation, use this

    // Loop as long as you can get 
    for (auto s = line(file); !s.empty(); s = line(file)) {
        auto videoId = stoi(s); // convert string to int
        auto title = line(file);
        auto genre = line(file);
        auto production = line(file);
        auto copies = stoi(line(file));

        // Verify output
        cout << videoId << ", "
                  << title << ", "
                  << genre << ", "
                  << production << ", "
                  << copies << "\n";
    }
}

Upvotes: 0

Jerry Coffin
Jerry Coffin

Reputation: 490098

As others have commented, fscanf is the wrong tool for this job. Even in C, using fscanf with the %s conversion would still be the wrong tool1.

As it stands now, your first fscanf will read and convert the first number (the video ID) correctly. But fscanf's %s conversion reads only a single, white-space delimited string, so the first one reads Raya and puts that into the video title. The second reads and, and puts that into the video genre. The third reads "the" and puts that into the production. Then the last tries to read the number of copies, but what it sees in the input buffer is Last, which won't convert to a number--so conversion fails. From there, nothing else stands any chance of working at all.

In this case, each of the strings occupies an entire line (with possible embedded spaces) so we want to read an entire line, not a single white-space delimited string. In C, we'd typically use fgets instead of fscanf for this job, and in C++ we normally want to use std::getline (though you can use fscanf with the scanset conversion like %[^\n], if you really want to).

There is one more bit that gets a little clumsy with a file format like this that has numbers mixed in with character strings. If you get a little careless about how you read the numbers, you can end up reading the number itself, but leaving the new-line immediately following the number in the input buffer. Then when you try to read the string on the next line, you actually end up reading an empty string at the end of the line that had the number on it. Then your code gets out of sync with the actual file, and doesn't work.

As a rule, I tend to deal with this by always reading an entire line at a time for the input file, then where I need a number, taking the string I read, and converting it to a number. This makes it much easier to assure that your reading stays in sync with the file format.


1. Note that `fscanf` using `%s` without specifying a maximum string length is inherently dangerous as well--if you're going to use it at all, at bare minimum you need to specify a maximum length. You also need to read into a normal array of char, not directly into a string (thus the advice to just avoid it completely in C++).

Upvotes: 3

Related Questions