-2

I am reasonably new to programming in C++ and i'm having some trouble reading data from a text file into an array of structures. I have looked around similar posts to try and find a solution however, I have been unable to make any of it work for me and wanted to ask for some help. Below is an example of my data set (P.S. I will be using multiple data sets of varying sizes):

00010 0
00011 1
00100 0
00101 1
00110 1
00111 0
01000 0
01001 1

Below is my code:

int variables = 5;

typedef struct {
    int variables[variables];
    int classification;
} myData;

//Get the number of rows in the file
int readData(string dataset)
{
    int numLines = 0;
    string line;
    ifstream dataFile(dataset);
    while (getline(dataFile, line))
    {
        ++numLines;
    }
    return numLines;
}

//Store data set into array of data structure
int storeData(string dataset)
{
    int numLines = readData(dataset);

    myData *dataArray = new myData[numLines];

    ...

    return 0;
}

int main()
{
    storeData("dataset.txt");

What I am trying to achieve is to store the first 5 integers of each row of the text file into the 'variables' array in the 'myData' structure and then store the last integer separated by white space into the 'classification' variable and then store that structure into the array 'dataArray' and then move onto the next row.

For example, the first structure in the array will have the variables [00010] and the classification will be 0. The second will have the variables [00011] and the classification will be 1, and so on.

I would really appreciate some help with this, cheers!

4
  • "I would really appreciate some help". Unfortunately, stackoverflow.com is not a web site where you get help with programming, this is a question and answer site, and a very careful examination of your question fails to find an actual, specific question being asked. P.S. "int variables[variables];", where "variables" is an int, is not valid C++, but a compiler-specific extension. Commented Nov 5, 2018 at 2:57
  • "the first 5 integers of each row" "00010" is not five integers but 5 chars or one integer. Also, why do you use new[] when there is std::vector<>? Commented Nov 5, 2018 at 2:59
  • I'd advise switching to a vector since, as you said, your items may be variable in size. You should also learn to declare your struct the C++ way, not as you would in C. If it was me I'd read a line at a time then split it into a string and int then loop over each character in the string and push it's integer value (char - '0') into the vector and assign the int to the int in the struct. Repeat until you run out of lines. The bonus of using a vector is you don't need to read the file twice to count the lines. Commented Nov 5, 2018 at 3:04
  • There are numerous similar questions on StackOverflow that might help you get started. Commented Nov 5, 2018 at 3:05

2 Answers 2

1

Provide stream extraction and stream insertion operators for your type:

#include <cstddef>    // std::size_t
#include <cstdlib>    // EXIT_FAILURE
#include <cctype>     // std::isspace(), std::isdigit()
#include <vector>     // std::vector<>
#include <iterator>   // std::istream_iterator<>, std::ostream_iterator<>
#include <fstream>    // std::ifstream
#include <iostream>   // std::cout, std::cerr, std::cin
#include <algorithm>  // std::copy()

constexpr std::size_t size{ 5 };

struct Data {
    int variables[size];
    int classification;
};

// stream extraction operator
std::istream& operator>>(std::istream &is, Data &data)
{
    Data temp;  // don't write directly to data since extraction might fail
               //  at any point which would leave data in an undefined state.

    int ch;  // signed integer because std::istream::peek() and ...get() return
            //  EOF when they encounter the end of the file which is usually -1.

                                                    // don't feed std::isspace
                                                   //  signed values
    while ((ch = is.peek()) != EOF && std::isspace(static_cast<unsigned>(ch)))
        is.get();  // read and discard whitespace

             // as long as
            //              +- we didn't read all variables
           //               |         +-- the input stream is in good state
          //                |         |      +-- and the character read is not EOF
         //                 |         |      |   
    for (std::size_t i{}; i < size && is && (ch = is.get()) != EOF; ++i)
        if (std::isdigit(static_cast<unsigned>(ch)))
            temp.variables[i] = ch - '0';  // if it is a digit, assign it to our temp
        else is.setstate(std::ios_base::failbit);  // else set the stream to a 
                                                  // failed state which will 
                                                 // cause the loop to end (is)

    if (!(is >> temp.classification))  // if extraction of the integer following the
        return is;                    // variables fails, exit.

    data = temp;  // everything fine, assign temp to data
    return is;
}

// stream insertion operator
std::ostream& operator<<(std::ostream &os, Data const &data)
{
    std::copy(std::begin(data.variables), std::end(data.variables),
              std::ostream_iterator<int>{ os });
    os << ' ' << data.classification;
    return os;

}

int main()
{
    char const *filename{ "test.txt" };
    std::ifstream is{ filename };

    if (!is.is_open()) {
        std::cerr << "Failed to open \"" << filename << "\" for reading :(\n\n";
        return EXIT_FAILURE;
    }

    // read from ifstream
    std::vector<Data> my_data{ std::istream_iterator<Data>{ is },
                               std::istream_iterator<Data>{} };

    // print to ostream
    std::copy(my_data.begin(), my_data.end(),
              std::ostream_iterator<Data>{ std::cout, "\n" });
}

Uncommented it looks less scary:

std::istream& operator>>(std::istream &is, Data &data)
{
    Data temp;
    int ch;

    while ((ch = is.peek()) != EOF && std::isspace(static_cast<unsigned>(ch)))
        is.get();

    for (std::size_t i{}; i < size && is && (ch = is.get()) != EOF; ++i)
        if (std::isdigit(static_cast<unsigned>(ch)))
            temp.variables[i] = ch - '0';
        else is.setstate(std::ios_base::failbit);

    if (!(is >> temp.classification))
        return is;

    data = temp;
    return is;
}

std::ostream& operator<<(std::ostream &os, Data const &data)
{
    std::copy(std::begin(data.variables), std::end(data.variables),
              std::ostream_iterator<int>{ os });
    os << ' ' << data.classification;
    return os;

}
Sign up to request clarification or add additional context in comments.

Comments

0

It looks line you are trying to keep binary values as integer index. If that is the case, it will be converted into integer internally. You may need int to binary conversion again.

If you want to preserve data as is in the text file, then you need to choose either char/string type for the index value. For classification, it seems value will be either 0 or 1. So you can choose bool as data type.

#include <iostream>
#include <map>

using namespace std;

std::map<string, bool> myData;

int main()
{
    // THIS IS SAMPLE INSERT. INTRODUCE LOOP FOR INSERT.
    /*00010 0
    00011 1
    00100 0
    00101 1
    00110 1*/
    myData.insert(std::pair<string, bool>("00010", 0));
    myData.insert(std::pair<string, bool>("00011", 1));
    myData.insert(std::pair<string, bool>("00100", 0));
    myData.insert(std::pair<string, bool>("00101", 1));
    myData.insert(std::pair<string, bool>("00110", 1));

    // Display contents
    std::cout << "My Data:\n";
    std::map<string, bool>::iterator it;

    for (it=myData.begin(); it!=myData.end(); ++it)
        std::cout << it->first << " => " << it->second << '\n';

    return 0;
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.