File Streams: Int (?) Output

I have a file, file.txt. I must read data from this file, make some calculations, and then output that data into new.txt.

The content of file.txt (does NOT contain the 1st line with parentheses, that's just there for understanding the table arrangement):
1
2
3
4
5
(ID) (previous stock) (stock sold) (required stock)
item1 96 48 51
item2 321 163 201
item3 35 21 26
item4 164 151 161


The content of new.txt must have the ID, the current stock (equal to previous stock - stock sold), and the amount needed to bring the stock to the required amount. Thanks to previous forum posts and StackOverflow, I know how to read just the numbers, but I have no idea how to include item(#) because I haven't the slightest clue on how to mix and match integers and characters/strings.

The approach thus far: create a 2D vector, open the input file and read it into filestream fileIn, read each line into a string, read each line's data into a new vector lineData, then combine it and write it out to the filestream of new.txt.

The code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iterator>

using namespace std;

int main()
{
	// 2D vector; read all input file's data into here
    vector <vector <int>> dataToRead;

    // file to read data in from
	ifstream fileIn;
	fileIn.open("file.txt");

    string line;
    // read each line into string variable
    while (getline(fileIn, line))
    {
        vector <int> lineData;
        stringstream lineStream(line);

        int value;
        // Read an integer at a time from the line
        while(lineStream >> value)
        {
            // numbers in line -> vector
            lineData.push_back(value);
        }
        // combine lineData into dataToRead 
        dataToRead.push_back(lineData);
    }

	// copy out dataToRead into new.txt
	ofstream fileOut;
	fileOut.open("new.txt", ios::out | ios::binary);	
	ostream_iterator <int> output_iterator(fileOut, "\t");
	for (int i = 0; i < dataToRead.size(); i++) 
	{
		copy(dataToRead.at(i).begin(), dataToRead.at(i).end(),
			 output_iterator);
		fileOut << endl; // newline after each row
	}
	
	// close files
	fileIn.close(); 
	fileOut.close();

}
Last edited on
Hello syeare,

Is a 2D vector required? Do you have to keep track of the ID?

I think you have found that what you have will not work. You are trying to put a string into an "int", so whether you read directly from the file or use a string stream either will fail when you when the formatted input tries to store a string into an 'int".

If you need to keep and use the ID consider a struct and 1D vector of structs to work with.

Andy

Hello Andy,

The vector part is not required at all, but I do need to keep track of the ID. I was wondering how I would keep track of the ID with the vector approach. I don't know how I would use anything other than vectors. Aren't structs essentially just classes? I'm not sure about it, but is it even possible to use structs in this situation?
Hello syeare,

If the 2D vector is not required I would use a single vector of structs.

You may consider a struct as a simple class, but it is not a class. In a struct everything is public by default where as a class everything is private be default. I suppose one could think of a struct to a class is like a C style array to a vector. Very similar, but the class and vector can do more for you.

I was about to work on the struck. This will take a few minutes and I will show you what I am thinking of.

For what you have to read and keep track of a vector of structs is a good solution. Maybe not the best, but good.

Andy
Hello syeare,

With out a lot of thought and trying to use what you started with I came up with this.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iterator>

using namespace std;

struct ProductData
{
    std::string s_ID;
    std::vector<int> s_numbers;

    ProductData(){}
    ProductData(std::string ID, std::vector<int> numbers)
    {
        s_ID = ID;
        s_numbers = numbers;
    }
};

int main()
{
    // 2D vector; read all input file's data into here
    vector <ProductData> dataToRead;

    // file to read data in from
    ifstream fileIn;  // <--- Could also use ifstream fileIn("file.txt").
    fileIn.open("file.txt");

    // <--- Need to check that file is open.

    string line;
    // read each line into string variable
    while (getline(fileIn, line))
    {
        vector <int> lineData;
        stringstream lineStream(line);

        int value;
        std::string ID;

        lineStream >> ID;

        // Read an integer at a time from the line
        while (lineStream >> value)
        {
            // numbers in line -> vector
            lineData.push_back(value);
        }

        // combine lineData into dataToRead 
        dataToRead.emplace_back(ID, lineData);
    }

    // copy out dataToRead into new.txt
    ofstream fileOut;  // <--- Could also use "ofstream fileOut("new.txt")  // <--- "iso::out" is not needed. The "ofstream" already says output.
    fileOut.open("new.txt", ios::out | ios::binary);  // <--- Any reason for the binary mode?

    // <--- Need to check that file is open.
    
    ostream_iterator <int> output_iterator(fileOut, "\t");

    // <--- The for loop needs changed.
    //for (size_t i = 0; i < dataToRead.size(); i++)
    //{
    //    copy(dataToRead.at(i).begin(), dataToRead.at(i).end(), output_iterator);

    //    fileOut << endl; // newline after each row
    //}

    // close files  // <--- Files will close when the function looses scope and the dtor is called. IMHO it is good form if you leave.
    fileIn.close();
    fileOut.close();

	// A fair C++ replacement for "system("pause")". Or a way to pause the program.
	// The next line may not be needed. If you have to press enter to see the prompt it is not needed.
	//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
	std::cout << "\n\n Press Enter to continue: ";
	std::cin.get();

	return 0;  // <--- Not required, but makes a good break point.
}

See what you think.

Andy
Sorry for responding late, but there's parts where I don't understand. For example, why did you use the std:: prefix even though you already declared that you're using the std namespace? Why is the function on L15 made twice (empty first)? Why does the for loop have to be changed from an int to a size_t? Finally and most importantly, I'm sorry but it doesn't seem to work?
1) Though still I do not understand why you made the function on L15 twice, could it have to do with constructors?
2) Are you using the std:: prefix because you want to use the definition from the std namespace rather than the string header?
3) I forgot about this, but in the for loop on L65, it must be changed to a size_t because int is not the same type as what's being compared in the vector, right?
4) I still have no clue why it doesn't work
Last edited on
I would suggest using a structure that contains the proper types contained in the input file.

Next I would suggest you create a vector of this class to hold all the items in the file.

Lastly for now is there a reason you're trying to use binary mode for the output?

Something like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct Inventory
{
    char name[20]; // If binary mode not required use std::string.
    int previous;
    int sold;
    int required;
};

int main()
{
	// vector; read all input file's data into here
    std::vector<Inventory> inventory;

    // file to read data in from
	ifstream fileIn("file.txt");

    string line;
    // read each line into string variable
    while (getline(fileIn, line))
    {
        stringstream lineStream(line);
        Inventory temp;
        lineStream >> temp.name >> temp.previous >> temp.sold >> temp.required;

        inventory.push_back(temp);
    }

    // Add your code to write the file here. Prefer normal text if binary is not an assignment requirement.
}

I only used binary because that's how I saw it used in an example and I thought it was done so because there could be text encoding issues. Regarding structs, I have been reading http://www.cplusplus.com/doc/tutorial/structures/ , but I don't get how I should manipulate that vector to get my desired results. A for loop to iterate through inventory ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;

struct Stock
{
   string ID;
   int previous, sold, required;
};
istream & operator >> ( istream &in, Stock &stock ) { return in >> stock.ID >> stock.previous >> stock.sold >> stock.required; }
ostream & operator << ( ostream &out, const Stock &stock ) { return out << stock.ID << '\t' << stock.previous << '\t' << stock.sold << '\t' << stock.required; }

int main()
{
   vector<Stock> holding;
   ifstream in( "file.txt" );
   for ( Stock stock; in >> stock; ) holding.push_back( stock );
   for ( Stock &stock : holding ) cout << stock << '\n';
}

item1	96	48	51
item2	321	163	201
item3	35	21	26
item4	164	151	161

Hello syeare,

Sorry I ended going to bed after my last post.

For example, why did you use the std:: prefix even though you already declared that you're using the std namespace?
Using using namespace std;. This is not the best idea even though it seams to make your code easier to write. This may be useful now, but someday it WILL cause a problem. I have left out the using statement for so long that typing "std::" is now just habit. Putting it in the code with the using statement makes no difference to the compiler.

Why is the function on L15 made twice (empty first)?
It is not made twice. In a class or a struct the compiler will provide a default ctor, constructor, and a default dtor, destructor. Line 16 is an overloaded ctor and when you define this in a class or a struct the compiler no longer provides a default ctor, so you have to provide your own. In this program you can get by without a default ctor, but it is a good habit to get into.

The other part of this is since you have provided a default and overloaded ctor, but nothing for the dtor the compiler will provide a default dtor.

Why does the for loop have to be changed from an int to a size_t?
When you look at the line for (int i = 0; i < dataToRead.size(); i++) the size function on "dataToRead.size()" returns a "size_t" type response. By changing the "int" to a "size_t" the "i < dataToRead.size()" now have matching data types. The "int" does work, but produces a compiler warning about the data types not matching.

Finally and most importantly, I'm sorry but it doesn't seem to work?
What part does not work. Be more specific.

Going back to the struct. Defining the overloaded ctor is to work with dataToRead.emplace_back(ID, lineData);. As I understand it "emplace_back" will first construct an object of what it needs before using "push_back" to add it to the vector. By putting the 2 variables in the () you construct an object with the proper information before adding it to the vector.

"push_back" would work, but "emplace_back" is much easier. Using "push_back" you first would have to create an object of the struct like ProductData temp;, put the read information into the temp struct then use the "push_back" to add it to the vector.

Using "binary" for the output file is not always necessary. Since you read a ".txt" file you should write to a ".txt" file. When I left the "binary" mode and looked at the output file I noticed that the normal "CR/LF" had changed to just a "LF". This did not seam to make any difference just reading the file, but should you ever have a need to read this file, ("new.txt") it could be a problem.

The first clue is the ".txt" extension. This usually means there is no need for anything special, like "binary" mode.

The other part of using "binary" mode is that you usually read or write in chunks of data not individual variables. And in "binary" mode numbers can be stored differently than in text mode. Writing a string may not be a problem in "binary" mode, but trying to read it back could be a problem. Also there is the possibality that a string greater than 15 characters may not all write to a "binary" file.

Regarding structs, I have been reading http://www.cplusplus.com/doc/tutorial/structures/ , but I don't get how I should manipulate that vector to get my desired results. A for loop to iterate through inventory ?
That link is a start, but have a look at https://www.learncpp.com/ and specifically https://www.learncpp.com/cpp-tutorial/47-structs/

I have found this to be a good tutorial and a good place for reference.

As to the for loop I left that to see what you would come up with.

Right or wrong post your attempt and we can fix it.

In lastchance's example he overloaded the "<<" and ">>" operators. This is the better approach, but if you are not ready for this yet the for loop will work. I would suggest staying with the for loop for now until you have a better understanding of how to access the vector of structs and the vector inside the struct, ( hint, hint, nudge, nudge).

Andy
Topic archived. No new replies allowed.