简体   繁体   中英

How to read values and arrays from text file

I know how to read a file with ifstream etc. I am just stuck on this task where I have a header file full of constants and a text file with 3 variables (budget, hotelType, [event1, event2, …, eventn]).

#ifndef CONSTANTS_H_
#define CONSTANTS_H_


const string nameMap[] = { "Opening", "Soccer 1", "Soccer 2", "Soccer 3",
        "Track and Field 1", "Track and Field 2", "Track and Field 3",
        "Track and Field 4", "Swimming 1", "Swimming 2", "Gymnastics 1",
        "Gymnastics 2", "Basketball 1", "Basketball 2", "Closing" };
const int eventPriceMap[] = { 2000, 80, 160, 500, 80, 100, 120, 140, 100, 100, 60, 100,
        150, 300, 800 };

const int eventDateMap[] = { 0, 3, 6, 9, 1, 2, 3, 4, 5, 6, 7, 8, 5, 7, 9 };

const int eventQuota[] = {60, 47, 30, 22, 50, 52, 42, 25, 37, 20, 43, 34, 35, 30, 40};

const int hotelPriceMap[] = {160, 210, 320};

const int hotelQuota[] ={20, 25, 30};// per day

const int MAXEVENTS = 10;

const int MAXREQUESTS = 150;

const int NUMBEROFEVENTS = 15;

const int NUMBEROFDAYS = 10;

#endif /* CONSTANTS_H_ */
9020,4[2,0,5,14,10,4,3,13,1]
7805,5[13,3,12,12,0,9,7,10,6,1]
7075,5[3,2,4,9,7,0,1,5,6,14]
7679,4[0,4,14,1,3,12,5,10]
6356,3[7,3]
6874,5[14,0,4,10,9,3]
4715,4[9]
4784,5[11]
4321,3[5,3,8,9]
6469,5[7,6,6,14,12,5,2]
4838,4[1,2]
4103,3[14]
5904,5[5,4,6]
5775,3[10,14,14,8,7,3,4]
7070,4[1,4,6,11,13,3,2,5,14]
4605,3[6,10,1,8,7,3,3]
7484,4[11,5,14,2,6,7,8,1,0]

Within another file how would I go about reading this text document and saving it into Budget, hotelType, and [events]. I absolutely have no idea im still learning c++, thankyou to anyone who helps!

Edit: I dont think the constants header file is necessary for this. My apologies

There might be different approaches to reading formatted data in C++. The most straight-forward is to use capabilities of the input stream, that you've said you are familiar with. It can read integers for you, and you just need to manually skip all the separators.

Let's assume you store your data as an array of these structs:

struct Entity
{
    int budget;
    int hotel_type;
    std::vector<int> events;
};

And you need to populate std::vector<Entity> entities . If your data are passed to the standard input, the parsing code would be:

while (cin) {
    Entity entity;
    char separator;
    cin >> entity.budget >> separator >> entity.hotel_type >> separator;

    while (cin && separator != ']') {
        int event;
        cin >> event >> separator;
        entity.events.push_back(event);
    }

    if (cin)
        entities.push_back(std::move(entity));
}

This simple implementation does not check that the format is strictly as expected. Ie it simply reads a separating character into the separator variable. You can add a check that it is indeed a comma, or a square bracket.

Note this last if (cin) at the end. If we try reading data from a stream which does not have them (ie it has already deplenished), the internal eofbit flag is set to the stream. We check it by simply providing the string variable as a condition, because it has operator bool() defined, which checks the eofbit flag for us (and some other flags too). We need to check it after the reading to make sure the reading was successful.

You can see this code in action here: https://rextester.com/MDZDG18083

In this demo I'm using a custom std::stringstream wrapping a provided data, but the code will work on any provided input stream.

If I understand your question correctly, here is a solution to solve your problem. According to your file, you need three variables:

  1. budget, which is 1-d array
  2. hotelType, which is 1-d array as well
  3. events, which can be 2-d array

So based on this the solution could be:

budget[]  = {9020,7805,7075,7679,6356,6874,4715 ...}
hotelType[] = {4,5,5,4,3,5 ...}
events[][] = {{2,0,5,14,10,4,},{13,3,12,12,0,9,7,10,6,1},{3,2,4,9,7,0,1,14} ...}

Let me know if I'm on the right track, so that we can proceed to the implementation ...

EDIT

First Solution, using array:

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

int main()
{
   std::ifstream infile("file.txt");
   std::string line;
   int budget[100], hotelType[100], events[100][100], index = 0;
   while (std::getline(infile, line)){
       std::string num;
       int i = 0;
       for( ; i < line.length(); i++){
            if(line[i] != ',' && line[i] != '[' && line[i] != ']')
                num += line[i];
            else{
                budget[index] = std::stoi(num);
                num = "";
                break;
            }
       }
       i++;
       hotelType[index] = std::stoi(line.substr(i, 1));
       i++; i++;
       for(int j = 0; i < line.length(); i++){
            if(line[i] != ',' && line[i] != '[' && line[i] != ']')
                num += line[i];
            else{
                events[index][j] = std::stoi(num);
                num = "";
                j++;
            }
       }
       index++;
   }
   for(int i = 0; i < index; i++){
       std::cout<< i + 1 << "th: ";
       std::cout<< "\tBudget    : " << budget[i] << std::endl;
       std::cout<< "\tHotel Type: " << hotelType[i] << std::endl;
       std::cout<< "\tEvents    : " << std::endl;
       for(int j = 0; j < 5; j++)
           std::cout<< "\t\t" << events[i][j] << std::endl;
   }
   return 0;
}

it's a string, right? why you don't try to split by the comma transforming into arrays? you can read with buffer line per line and then split by comma (removing the brackets) and saving the values.

First split each line by ',' that return array whit 2 elements

first element is budget and second element is hotelType[event1, event2, …, eventn]

then you should get string between "[" , "]" and again split by , and that return array with multi length

I've tried to describe this solution by comments in the code. It basically just defines stream operators to be able to read/write the data from any stream.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ios>
#include <sstream>
#include <stdexcept>

// create an alias for a number of events using a standard container
// for int's, std::vector<int>
using EventList = std::vector<int>;

// add an input stream operator for our EventList to be able to read
// and split strings looking like  [int,int,...,int]
std::istream& operator>>(std::istream& is, EventList& el) {
    std::string tmp; // a temporary string
    el.clear();      // remove any existing events

    if(std::getline(is, tmp)) { // read until end-of-line
        // check that we got [ and ]
        if(tmp.size()<2 || tmp[0] != '[' || tmp[tmp.size()-1] != ']') {
            // wrong format, set the input streams failbit
            is.setstate(std::ios::failbit);
        } else {
            // remove the first and last character  [ and ]
            tmp = tmp.substr(1, tmp.size()-2);
            // put the string in a string stream to be able
            // to use std::getline with a delimiter
            std::stringstream ss(tmp);
            // loop until the stringstream is empty
            while( std::getline(ss, tmp, ',') ) {
                // tmp should now be a string of digits
                // use stoi to convert the string to an int
                try {
                    int an_int = std::stoi(tmp);
                    // put the int at the back of our EventList
                    el.emplace_back(an_int);
                } catch(const std::invalid_argument& ex) {
                    // the conversion to an int failed
                    // set the input streams failbit
                    is.setstate(std::ios::failbit);
                }
            }
        }
    } else {
        // getline failed, set the failbit on the stream
        is.setstate(std::ios::failbit);
    }
    return is;
}

// add an output stream operator for EventList to be able to output
// it in the same format as it was read
std::ostream& operator<<(std::ostream& os, const EventList& el) {
    os << "[";
    if(el.size()) { // check that we have events to stream
        // create an iterator that points at the first element
        EventList::const_iterator it = el.begin();
        // dereference the iterator to get the int it points to
        // and stream that int
        os << *it;
        ++it; // step to the next event
        // loop until the iterator points beyond the last element
        // in the EventList
        for(;it!=el.end(); ++it) {
            // prepend each event with a comma
            os << "," << *it;
        }
    }
    os << "]";
    return os;
}

// here's an operator for a vector of EventList's for convenience
// it follows the same pattern as the operator above
std::ostream& operator<<(std::ostream& os, const std::vector<EventList>& vel) {
    os << "[";
    if(vel.size()) {
        std::vector<EventList>::const_iterator it = vel.begin();
        os << *it;
        ++it;
        for(;it!=vel.end(); ++it) {
            os << " " << *it;
        }
    }
    os << "]";
    return os;
}

// one line in your file      int,int[int...]
// broken down into a struct
struct OneDataLine {
    int budget;
    int hotelType;
    EventList events;   // using the alias created above

    // the default constructor with initialization of members
    OneDataLine() :
        budget(0),
        hotelType(0),
        events()
    {}
    // declaring stream operators as friends, allowing them to operate
    // on the objects members. this is not really necessary
    // since this struct has all members public but if you later decide
    // to make them private, this will come in handy
    friend std::istream& operator>>(std::istream&, OneDataLine&);
    friend std::ostream& operator<<(std::ostream&, const OneDataLine&);
};

// an input stream operator for reading one data line
std::istream& operator>>(std::istream& is, OneDataLine& d) {
    char separator;
    is >> d.budget >> separator >> d.hotelType;
    if(separator != ',') {
        // if the separator between budget and hotelType is not
        // a comma, set the input stream in a failed state
        is.setstate(std::ios::failbit);
    } else {
        // we should now only have the events part left on
        // the line. stream it to the EventList for which we
        // have already added an input stream operator
        is >> d.events;
    }
    return is;
}

// an output stream operator for writing one data line
std::ostream& operator<<(std::ostream& os, const OneDataLine& d) {
    // int's have built-in stream operators
    // and we have also added an output stream operator for
    // EventList so streaming becomes easy
    os << d.budget << "," << d.hotelType << d.events;
    return os;
}

// USAGE: progname datafile1 ... datafileX
int main(int argc, char* argv[]) {
    // convert C style main() parameters to a C++ container.
    // we use std::vector again, but this time for storing
    // strings
    std::vector<std::string> args(argv+1, argv+argc);
    // yet another vector, but this is for storing data lines
    std::vector<OneDataLine> all_data_lines;

    // Reading part

    // loop over the input parameters to main()
    for(const std::string& file : args) {
        std::fstream fs(file); // open file for reading
        // loop over the opened file for as long as the
        // filestream's failbit isn't set
        while(fs) {
            // a temporary instance of OneDataLine
            OneDataLine tmp;
            // stream from the open file to our temporary
            fs >> tmp;
            // if the failbit still isn't set, move the
            // content of the temporary variable into
            // our vector of data lines
            if(fs) all_data_lines.emplace_back(std::move(tmp));
        }
        // the filestream will be automatically closed
        // when it goes out of scope
    }

    // Writing part

    // loop over all the data lines we've collected and
    // stream to cout. we could just as well stream to
    // a file opened for writing
    for(const OneDataLine& line : all_data_lines) {
        // stream the complete data line using our own output
        // stream operator for OneDataLine
        std::cout << line << "\n";

        // stream individual members too
        std::cout << " budget   : " << line.budget << "\n";
        std::cout << " hotelType: " << line.hotelType << "\n";
        std::cout << " events   : " << line.events << "\n";
        // and stream each event separately
        std::cout << "          [\n";
        for(const int& ev : line.events) {
            std::cout << "            " << ev << "\n";
        }
        std::cout << "          ]\n";
    }

    // Creating containers for each category
    std::vector<int> budgets;
    std::vector<int> hotelTypes;
    std::vector<EventList> event_lists;
    // loop through the collected data and put each member in
    // the container for its category
    for(const OneDataLine& line : all_data_lines) {
        budgets.push_back(line.budget);
        hotelTypes.push_back(line.hotelType);
        event_lists.push_back(line.events);
    }
    // Output categorized containers

    // here we use EventList's (std::vector<int>'s) output stream operator
    std::cout << "budgets    : " << budgets << "\n";
    std::cout << "hotelTypes : " << hotelTypes << "\n";
    // and here we use our convenience output stream operator for
    // a vector of EventList
    std::cout << "event_lists: " << event_lists << "\n";
    return 0;
}

Sample output:

% progname datafile
[...skipping to the end...]

7484,4[11,5,14,2,6,7,8,1,0]
 budget   : 7484
 hotelType: 4
 events   : [11,5,14,2,6,7,8,1,0]
          [
            11
            5
            14
            2
            6
            7
            8
            1
            0
          ]
budgets    : [9020,7805,7075,7679,6356,6874,4715,4784,4321,6469,4838,4103,5904,5775,7070,4605,7484]
hotelTypes : [4,5,5,4,3,5,4,5,3,5,4,3,5,3,4,3,4]
event_lists: [[2,0,5,14,10,4,3,13,1] [13,3,12,12,0,9,7,10,6,1] [3,2,4,9,7,0,1,5,6,14] [0,4,14,1,3,12,5,10] [7,3] [14,0,4,10,9,3] [9] [11] [5,3,8,9] [7,6,6,14,12,5,2] [1,2] [14] [5,4,6] [10,14,14,8,7,3,4] [1,4,6,11,13,3,2,5,14] [6,10,1,8,7,3,3] [11,5,14,2,6,7,8,1,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