0

My professor does not actually show you how to do anything the assignments require, so here I am using the internet to help me. I have already googled how to do this and it was of no help haha. The question is:

Suppose that the file inData.txt contains the following data:

Giselle Robinson Accounting
5600 5 30

The first line contains a person’s first name, last name, and the department the person works in. In the second line, the first number represents the monthly gross salary, the bonus (as a percent), and the taxes (as a percent). Read the data from the file inData.txt and output/echo the exact same date to the output file outData.txt.

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • What specifically are you having trouble with? It doesn't appear that you need to manipulate the data at all so open both files, read a line from the first and write it to the second until there are no more lines to read. – Retired Ninja Oct 13 '20 at 02:35
  • You can use `std::ifstream` and `std::ofstream` to handle file i/o, among other options. – Telescope Oct 13 '20 at 02:40
  • [Option 2 of this answer should get you started](https://stackoverflow.com/a/7868998/4581301). Note that you need to do it twice, one for each line in the record. You may want to make a `class` or `struct` to hold the record (it's always good to aggregate data into an easy-to-use structure) it and then [overload `>>` ](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) to read into the `class` or `struct` with less fuss. The reading loop can be as simple as `struct record; while (in >> record) { use record }` – user4581301 Oct 13 '20 at 03:28

2 Answers2

0

There isn't ever an exact duplicate to get you started, but taking things piece-by-piece using cppreference.com as your reference you will begin to work your way through the problem.

Your task here presents a couple of challenges. You will need to open and validate both an input stream and output stream to read and write data to a file. While you can simply open the input file for reading, there are several output file modes you need to consider std::basic_ofstream::open. After opening each file you want to validate that each file is open std::basic_fstream::is_open

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

int main (int argc, char **argv) {
    
    if (argc < 3) { /* validate arguments for infile and outfile names given */ 
        std::cerr << "error: insufficient input\n"
                    "usage: program infile outfile\n";
        return 1;
    }
    
    std::ifstream infile (argv[1]);     /* open input file */
    if (!infile.good()) {               /* validate it is open for reading */
        std::cerr << "error: file open failed '" << argv[1] << "'\n";
        return 1;
    }
    
    /* open output file */
    std::ofstream outfile (argv[2], std::ios::out | std::ios::trunc);
    if (!outfile.good()) {              /* validate it is open for writing */
        std::cerr << "error: file open failed '" << argv[2] << "'\n";
        return 1;
    }

You have two lines of data to read from the file. The first is the name which you will want to keep together and can be read into a std::basic_string. The next line has salary, bonus and taxes all on one line. Here you have two options:

  1. attempt to read from the file using the >> operator into each of salary, bonus and taxes -- but that will leave you with the problem of the '\n' remaining unread in the input stream needing to std::basic_istream::ignore the remaining characters in the line before your next attempted read of the next name; or
  2. you read the next line exactly the same way you did the first, into a std::string, which will consume the entire line leaving the input stream read for your next attempted read, but then you will need split the line into salary, bonus and taxes using std::basic_stringstream

There are a number of advantages with using the second option. You read your first line and assign the result to name and you validate line.length() (or name.length()) is not zero to ensure you have read a valid name.

If so you proceed to read the next line -- the exact same way and create a std::stringstream from the line you read and then validate the read of salary, bonus and taxes from the std::stringstream using the >> operator -- knowing that regardless the result of the read from the std::stringstream, your infile is read for your next attempted read of name.

On a successful read of salary, bonus and taxes from the std::stringstream you can write name, salary, bonus and taxes to outfile and loop to read your next name (and repeat until your run out of name and salary, bonus and taxes lines to read.

    std::string line {};                            /* string to hold line */
    
    while (getline (infile, line)) {                /* read name line, validate */
        if (!line.length()) {                       /* if line is empty, bail */
            std::cerr << "error: name empty\n";
            break;
        }
        std::string name = line;                    /* assign line to name */
        unsigned salary, bonus, taxes;              /* vars for rest */
        
        if (!getline (infile, line)) {              /* read line of salary, bonus, taxes */
            std::cerr << "error: no data following '" << name << "'\n";
            break;
        }
        std::stringstream ss(line);                 /* create stringstream from line */
        
        /* read values from stringstream -- validate */
        if (!(ss >> salary >> bonus >> taxes)) {
            std::cerr << "error: reading salary, bonus taxes\n";
            break;
        }
        
        /* write result to outfile */
        outfile << name << '\n' << salary << " " << bonus << " " << taxes << '\n';
    }
}

(note: those are the two halves of the complete program)

Example Input File

If you have an input file of:

$ cat dat/infile.txt
Giselle Robinson Accounting
5600 5 30

Compile With Warnings Enabled

Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. They will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.

$ g++ -Wall -Wextra -pedantic -Wshadow -std=c++14 -Ofast -o bin/infileoutfile infileoutfile.cpp

Example Use

You can provide both the input and output filenames as the first two arguments to your program:

$ ./bin/infileoutfile dat/infile.txt dat/outfile.txt

Resulting Output File

Resulting in the output file created:

$ cat dat/outfile.txt
Giselle Robinson Accounting
5600 5 30

Now this isn't the only way to approach reading your file and creating the output, but these are the considerations and thought process you should be working through approaching any of your new projects. Look things over and let me know if you have further questions.

Further Readings

Why is “using namespace std;” considered bad practice? and should you ever be tempted Why !.eof() inside a loop condition is always wrong. and also worth understanding C++: “std::endl” vs “\n”

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

If you have used cout and cin then the concept of file input and output is exactly the same. Instead of doing stuff on command line, it happens on files. Here is the code:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ifstream fin("inData.txt");                 //Input file
    if (!fin){ //This will check if file has been opened
        cout<<"File not found!";
        return 0;
    }
    ofstream fout("outData.txt");               //output file

    string fname,lname,dept;                    //string variables for text
    int sal,bonus,tax;                          //integers for numbers in file
    
    fin>>fname>>lname>>dept;                    //grabbing the data
    fin>>sal>>bonus>>tax;
    
    fout<<fname<<" "<<lname<<" "<<dept<<"\n";   //writing the data
    fout<<sal<<" "<<bonus<<" "<<tax<<"\n";

    fin.close();                                //close the files
    fout.close();
    return 0;
}

The lines like this fin>>fname>>lname>>dept; pick data up in each variable based on white-spaces or simple spaces. When the first space will be encountered, fname will get its data and so on. If you try to read int in string, it will be okay but vice versa is not allowed, you will get error.

abdullahQureshee
  • 322
  • 2
  • 12
  • That will work, but it is horribly fragile. What happens with `"Giselle J. Robinson Accounting"`? If you want to separate the Department, better to read the line and then use `line.find_last_of(" "));` verify the next character `isalpha()` and then separate the line at that point. – David C. Rankin Oct 13 '20 at 06:29
  • Also, do not hardcode filenames or use *Magic Numbers* in your code. If you need the filenames, pass them as arguments to your program or prompt the user for input. You shouldn't have to recompile your code just because you want to read a different file. – David C. Rankin Oct 13 '20 at 06:38
  • Totally agreed and your answer (code) is the way to go. But I don't think that a considerable amount of your code was understandable to the OP as he seems very new to programming. I see that you are far more experienced in Programming and I totally agree with you but if you look back when you first learnt file handling, this code would have done. I posted this simpler code because of the above mentioned reasons. Now its up to OP what suits him more. – abdullahQureshee Oct 13 '20 at 06:44
  • Yes, no dings for it. When answer for a new programmer, I always try and make sure I don't leave them with habits that will be hard to break later (through I'm not sure I'm always successful `:)` – David C. Rankin Oct 13 '20 at 07:06