-1

I am working on a project and have gotten completely stuck. Basically I have to read input from a file and print to an output file. I have already done all this, but now, the variables are supposed to be arrays, and I am stuck.

Here are the instructions:

Add a new function: collectData

(a) This function should take in the plans, comics, and input file stream as parameters

(b) It should contain a loop that calls getPlan and getComics until the arrays are filled up

(c) You should use some sort of counter variable to track which array element you're on.

here is my code: (I am confused on how to fill the arrays)

void collectData(char plan[], ifstream& fin, int SIZE) {
    while (!fin.eof( )) {
        getPlan(plan[],fin);
        for(int i=1; i<SIZE ; i++) {
            plan[i] = ;
        }
    }
}
Francis Cugler
  • 7,462
  • 1
  • 24
  • 44
  • 2
    Bug here: `while (!fin.eof( )) `. Description of why here: [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) – user4581301 Dec 16 '18 at 20:26
  • Start by making lists of the tasks that need to be performed to get the behaviour you want. If you don't know how to perform a task, break the task down into smaller and smaller tasks until you do know how to do them. This leaves you with a tree. You work your way from the leaves of the tree back to the root accomplishing all of the tasks one by one, compiling and testing as you go. When you get back to the root, you have a program and it probably works. – user4581301 Dec 16 '18 at 20:29
  • 1
    Interesting fun fact: If you place the streams in the narrowest required scope, the streams will go out of scope when they aren't required anymore and will close themselves. This is [a facet of RAII](https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii), one of the most important concepts in C++. – user4581301 Dec 16 '18 at 20:32
  • How to save this question from an early demise: 1) Focus on exactly one problem. If you ask three, your question is too broad. 2) Make an attempt at solving the problem so that we can see where you are coming from and what kind/how much help you require. Without an attempt your problem is again too broad and lacking a [mcve]. – user4581301 Dec 16 '18 at 20:35
  • Recommendation: when the program fails, do not `exit(0);`. 0 is what's returned by a program that operated correctly. If the exit was due to an error, return some other number and document those numbers so that users will know what happened and have an easier time correcting it. – user4581301 Dec 16 '18 at 20:37
  • I have reduced my question to one specific function. however, NOW i cannot seem to post the code correctly even though I've indented 4 spaces. I am now confused on 2 things.... fml –  Dec 16 '18 at 20:52
  • seems `count` is invariant, it is just passed in everywhere. is that on purpose? getplan takes both plan[] as an argument and returns one entry in plan[]? – AndersK Dec 16 '18 at 21:05
  • @Anders That's the thing. I am VERY new to c++, and this is confusing me to no end. I am trying to follow the instructions, but I do not understand how to call getPlan() and getComics() to "fill the arrays" –  Dec 16 '18 at 21:26
  • in order for people to help you, you need to provide information about your data structures and how those data structures are is represented in the file. btw while (!fin.eof()) is not correct, the eof flag is set *after* you reach eof, not before. – AndersK Dec 17 '18 at 05:20

1 Answers1

1

Typically when I'm reading and writing data from files, especially when reading them; I like to create a Data Structure that will represent the information that I'm pulling from the file. You will have to know how the file is structured in order to read the contents from it. Typically there are 3 different ways; you can read a single line at a time, you can read line by line until all lines have been read, or you can read everything from the file all in one go. There are ways to read different amount of bytes but that's a little more complicated and beyond the scope of this design process. What I normally do is; I'll read the contents from the file and store them into either a string, or a set of strings. Then after I retrieved the information from the file; I can then close it and be done with it. Once I have that information stored; then I will parse the string data, and from there I will then populate my Data Structures on the parsed data. I like breaking things down into individual functions to separate their logic and responsibility.

Your code structure may look something like this:

struct MyDataType {
    // the contents that you will store from a file.
};

// A basic method to split a string based on a single delimiter
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream( s );
    while( std::getline( tokenStream, token, delimiter ) ) {
        tokens.push_back( token );
    }

    return tokens;
}

// Similar to above but with the ability to use a string as a delimiter as opposed to just a single char
std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true ) {
    std::vector<std::string> tokens;
    if( strDelimiter.empty() ) {
        tokens.push_back( strStringToSplit );
        return tokens;
    }

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if( keepEmpty || !strTemp.empty() ) {
            tokens.push_back( strTemp );
        }

        if( itSubStrEnd == strStringToSplit.end() ) {
            break;
        }
        itSubStrStart = itSubStrEnd + strDelimiter.size();
    }
    return tokens;
}

// This function will open a file, read a single line
// closes the file handle and returns that line as a std::string
std::string getLineFromFile( const char* filename ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;
    std::getline( file, line );

    file.close();

    return line;
}

// This function will open a file and read the file line by line
// storing each line as a string and closes the file then returns
// the contents as a std::vector<std::string>
void getAllLinesFromFile( const char* filename, std::vector<std::string>& output ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;
    while( std::getline( file, line ) ) {
        if( line.size() > 0 )
            output.push_back( line );
    }
    file.close();
}

// This function will open a file and read all of the file's contents and store it into
// a large buffer or a single string.
void getDataFromFile( const char* filename, std::string& output ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::stringstream buf;
    buf << file.rdbuf();
    output.clear();
    output.reserve( buf.str().length() );
    output = buf.str();
} 

// The declaration of this can vary too; depending on if you are doing a single line
// from the file, doing single line at a time for the entire file, or reading all
// of the contents from a large buffer.
void parseDataFromFile( const std::string& fileContents, std::vector<std::string>& output, std::vector<MyDataStructure>& data ) {
    // This will vary on the file's data structure,
    // but this is where you will call either of the `splitString` functions
    // to tokenize the data.
    // You will also use the `std::string's` conversion utilities such as
    // std::stoi(...)... to convert to your basic types
    // then you will create an instance of your data type structure
    // and push that into the vector passed in. 
}

Then your main would look something like this: I'll use the line by line version

int main() {
    try {
        std::string fileContents;
        getAllinesFromFile( "test.txt", fileContents );
        std::vector<std::string> tokens;
        std::vector<MyDataStructure> data;
        parseDataFromFile( fileContents, tokens, data );
    } catch( std::runtime_error& e ) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

This allows the code to be readable, more modular, reusable, and in some ways even generic. It also helps to keep minimize the amount of debugging, and lessens the code management.

-Note- Also if you looked carefully at my functions where I'm reading in the data from the file; you will not see while( !file.eof() )! This is bad code practice! The best way is to either use std::getline(...) or the stream << operators within a while loop to read in the data.

Francis Cugler
  • 7,462
  • 1
  • 24
  • 44