简体   繁体   中英

Understanding reading txt files in c++

I am trying to understand reading different txt file formats in c++

I am currently trying to read a file formatted like this,

val1 val2 val3
val1 val2 val3
val1 val2 val3

When I read the file in and then cout its contents I only get the first line then a random 0 0 at the end.

I want to save each value into its own variable in a struct.

I am doing this like this,

struct Input{
    std::string group;
    float total_pay;
    unsigned int quantity;

    Input(std::string const& groupIn, float const& total_payIn, unsigned int const& quantityIn):
    group(groupIn),
    total_pay(total_payIn),
    quantity(quantityIn)
    {}
};

int main(){

    std::ifstream infile("input.txt");
    std::vector<Input> data;

    std::string group;
    std::string total_pay;
    std::string quantity;

    std::getline(infile,group);
    std::getline(infile,total_pay);
    std::getline(infile,quantity);

    while(infile) {
        data.push_back(Input(group,atof(total_pay.c_str()),atoi(quantity.c_str())));

        std::getline(infile,group);
        std::getline(infile,total_pay);
        std::getline(infile,quantity);
    }


    //output
    for(Input values : data) {
        std::cout << values.group << " " << values.total_pay << " " << values.quantity << '\n';
    }

    return 0;
}

What is the proper way to read this file in the the format I have specified? Do I need to specify to go to the next line after the third value?

Or should this be taking each value and putting them in to the right variable?

std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);

Your input processing has a number of issues. Your prevalent usage of std::getline in places where it is not needed isn't helping.

In short, per-line validation of input is generally done with a model similar to the following. Note that this requires the class provide a default constructor. We use an input-string-stream to process a single item from each line of input from the input file. If it was certain there was at-most one per line, we could forego the per-line processing, but it is a potential place for errors, so better safe than sorry. The mantra presented here is commonly used for per-line input validation when reading a stream of objects from a formatted input file, one item per line.

The following code defines the structure as you have it with a few extra pieces, including providing both an input and output stream insertion operator. The result makes the code in main() much more manageable.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>

struct Input
{
    // friends not needed if the members are public, but provided here
    //  in case you ever do make them protected or private (which you should)
    friend std::istream& operator >>(std::istream& inp, Input& item);
    friend std::ostream& operator <<(std::ostream& outp, Input const& item);

    std::string group;
    float total_pay;
    unsigned int quantity;

    // default constructor. sets up zero-elements
    Input() : total_pay(), quantity()
    {
    }

    Input(std::string groupIn, float total_payIn, unsigned int quantityIn)
        : group(std::move(groupIn))
        , total_pay(total_payIn)
        , quantity(quantityIn)
    {
    }

    // you really should be using these for accessors
    std::string const& getGroup() const { return group; }
    float getTotalPay() const { return total_pay; }
    unsigned int getQuantity() const { return quantity; }
};

// global free function for extracting an Input item from an input stream
std::istream& operator >>(std::istream& inp, Input& item)
{
    return (inp >> item.group >> item.total_pay >> item.quantity);
}

// global operator for inserting to a stream
std::ostream& operator <<(std::ostream& outp, Input const& item)
{
    outp << item.getGroup() << ' '
         << item.getTotalPay() << ' '
         << item.getQuantity();
    return outp;
}

int main()
{
    std::ifstream infile("input.txt");
    if (!infile)
    {
        std::cerr << "Failed to open input file" << '\n';
        exit(EXIT_FAILURE);
    }

    // one line per item enforced.
    std::vector<Input> data;
    std::string line;
    while (std::getline(infile, line))
    {
        std::istringstream iss(line);
        Input inp;
        if (iss >> inp) // calls our extaction operator >>
            data.emplace_back(inp);
        else
            std::cerr << "Invalid input line: " << line << '\n';
    }

    // dump all of them to stdout. calls our insertion operator <<
    std::copy(data.begin(), data.end(),
              std::ostream_iterator<Input>(std::cout,"\n"));
    return 0;
}

Provided the input is properly formatted, values like this:

group total quantity
group total quantity

will parse successfully. Conversely, if this happens:

group total quantity
group quantity
group total quantity
total quantity

the extractions of the second and fourth items will fail, and appropriate warning will be issued on std::cerr . This is the reason for using the std::istringstream intermediate stream object wrapping extraction of a single line per item.

Best of luck, and I hope it helps you out.

Check this solution

It is without error checks but with conversion to types

#include<iostream>
#include<sstream>
using namespace std;
int main()
{
    string line="v1 2.2 3";//lets say you read a line to this var...
    string group;
    float total_pay;
    unsigned int quantity;
    //we split the line to the 3 fields
    istringstream s(line);
    s>>group>>total_pay>>quantity;
    //print for test
    cout<<group<<endl<<total_pay<<endl<<quantity<<endl;
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM