C++ fstream writing to file very slow - c++

I have a 2D world in my game consisting of tiles. Whenever I make a new world I initialize an array of 48 million tiles
short worldGrid[48000000];
I set the value for all 48 million, then I write those values into a file like this:
std::fstream save("game_save", std::fstream::out);
for (int x = 0; x < 48000000; x++)
{
save << world.worldGrid[x];
save << " ";
}
save.close();
It's 48 million values, each one 2 bytes. So 96 million bytes, or 96 megabtyes. My problem is that this process inside the for loop alone takes 2 minutes to complete on my SSD. I don't feel like it should take 2 minutes and 5 seconds to write 96mb worth of data onto this file. If anyone has any advice I'd really appreciate it.

Try writing the array all at once, instead of 2 bytes-at-a-time..
Something like:
save.write(world.worldGrid, sizeof(worldGrid));
See the docs

Related

Moving through text file c++

I'm trying to save numbers from first txt file to second one in reversed order.
To be clear, inside 1st txt I have typed numbers from 1 to 10 (decimal notation). When I try to count them, I get 5 or 7, depending on what's between them (space or enter).
Then, another error is that inside 2nd txt program saves as much "0s" as dl's variable value is equal to instead of loaded numbers in reversed order.
I paste the whole code, because I don't know file operation rules good enough to determine which exact part could be the source of problem. Thank You in advance.
#include <fstream>
#include <iostream>
using namespace std;
int main() {
fstream plik1;
plik1.open("L8_F3_Z2a.txt", ios::in | ios::binary);
fstream plik2;
plik2.open("L8_F3_Z2b.txt", ios::out);
if(!plik1.good() || !plik2.good()) {
cout << "file(s) invalid" << endl;
return 1;
}
plik1.seekg(0, ios::end);
int dl = plik1.tellg() / sizeof(int);
cout << "length = " << dl << endl;
int a;
for(int i = 0; i < dl; i++) {
plik1.seekg((i + 1) * sizeof(int), ios::end);
plik1 >> a;
plik2 << a;
cout << i + 1 << ". a = " << a << endl;
}
plik1.close();
plik2.close();
return 0;
}
edit the output is:
length = 7
1. a = 0
2. a = 0
3. a = 0
4. a = 0
5. a = 0
6. a = 0
7. a = 0
--------------------------------
Process exited after 0.03841 seconds with return value 0
Press any key to continue . . .
Problem
When a file is encoded as text the binary size of the data is irrelevant.
int dl = plik1.tellg() / sizeof(int);
will get you the side of the file in integers, but the file isn't storing integers. It is storing a stream of characters. Say for example the file holds one number:
12345
which is five characters long. Assuming the file is using good ol ASCII, that's 5 bytes. When 12345 is converted to an int it will probably be 4 or 8 bytes and almost certainly not 5 bytes. Assuming the common 32 bit (4 byte) int
int dl = plik1.tellg() / sizeof(int);
int dl = 5 / 4;
int dl = 1;
Yay! It worked! But only by the grace of whatever deity or cosmic entity you worship. Or don't worship. I'm not going to judge. To show why you can't count on this, lets look at
123
this is three characters and 3 bytes, so
int dl = plik1.tellg() / sizeof(int);
int dl = 3 / 4;
int dl = 0;
Whoops.
Similarly
1 2 3 4 5
is five numbers. The file length will probably be the sum of one byte per digit and one byte per space, 9 bytes.
Where this gets weird is some systems, looking at you Windows, use a two character end of line marker, carriage return and a line feed. This means
1
2
3
4
5
will sum up to 13 bytes.
This is why you see a different size depending on whether the numbers are separated with spaces or newlines.
Solution
The only way to find out how many numbers are in the file is to read the file, convert the contents to numbers, and count the numbers as you find them.
How to do that:
int num;
int count = 0;
while (plik1 >> num) // read numbers until we can't read any more
{
count++;
}
From this you can determine the size of the array you need. Then you rewind the file, seek back to the beginning, allocate the array and read the file AGAIN into the array. This is dumb. File IO is painfully slow. You don't want to do it twice. You want to read the file once and store as you go without caring how many numbers are in the file.
Fortunately there are a number of tools built into C++ that do exactly that. I like std::vector
std::vector<int> nums;
int num;
while (plik1 >> num)
{
nums.push_back(num);
}
vector even keeps count for you.
Next you could
std::reverse(nums.begin(), nums.end());
and write the result back out.
for (int num: nums)
{
plik2 << num << ' ';
}
Documentation for std::reverse
If your instructor has a no vector policy, and unfortunately many do, your best bet is to write your own simple version of vector. There are many examples of how to do this already on Stack Overflow.
Addendum
In binary 5 integers will likely be 20 or 40 bytes no matter how many digits are used and no separators are required.
It sounds like storing data as binary is the bees knees, right? Like it's going to be much easier.
But it's not. Different computers and different compilers use different sizes for integers. All you are guaranteed is an int is at least 2 bytes and no larger than a long. All of the integer types could be exactly the same size at 64 bits. Blah. Worse, not all computers store integers in the same order. Because it's easier to do some operations if the number is stored backwards, guess what? Often the number is stored backwards. You have to be very, very careful with binary data and establish a data protocol (search term for more on this topic: Serialization) that defines the how the data is to be interpreted by everyone.

Need Help in a Project of C++

So this is the actual Problem
Can anyone tell me that how I read the repective Data from the file, and how would I able to store it in variables (without using array) also the code should be generic, That if the number of series will incresed or decresed.. Code will not be affected... I Just can't understand that how would I store sata in variables and how.. Please Help.. :(
Problem
A file contains information of a batsman. Information is no of series
played by the batsman. No of matches played in each series & score in
each match by the batsman. You have to read the data (without using
any array) and find average score and maximum score in all matches of
a series. In the end find overall average score and max score in all
matches.
Input:
Read data from file "cricket.txt". First line contains no of seasons/
series played by the player. Next pair of lines contains matches
played by the batsman followed in next line scores by batsman in
different matches of a season. See sample "cricket.txt"
5
6
93 75 41 40 90 19
5
45 86 30 60 29
3
47 90 33
4
22 2 92 5
5
88 67 96 91 90
First 5 shows player has played 5 seasons/ series
Next 6 show in first series player has played 6 matches
Next line has scores of player in 6 matches
Next 5 show in second series player has played 5 matches
Next line has scores of player in 5 matches
So on in second last line 5 shows player has played 5 matches in 5th
series
Last line has scores of player in 5 matches of last series
You're looking for an array.
int a[10];
// Loop that assigns all elements in array a to 0
for (int i = 0; i < 10; i++)
{
a[i] = 0;
}
// Array b will have all of it's members initialized to 0
int b[10]{};
// You can also assign different values to different elements of the array
b[0] = 6;
b[8] = 2;
// You can then use the array elements in operations
int c = b[0] * b[8];
If you want array like structure without compile time defined size, then use std::vector.
// An empty vector of ints
std::vector<int> d;
// A simple int
int e = 5;
// Push 2 values to the end of the vector
d.push_back(2);
d.push_back(e);
// Use the members for operations
int f = d.at(0) * d.at(1);
Since you've now described the problem you're trying to solve instead of just the problem with the solution you came up with:
You don't need to invent variable names or use arrays to compute averages and maximums.
Here's an example of how you can compute an average of the numbers a user inputs:
float sum = 0;
int elements = 0;
float input = 0;
while (cin >> input)
{
sum += input;
elements += 1;
}
std::cout << "Average: " << sum / elements << std::endl;
It's easy to expand this to also keep track of the maximum value so far.
To expand to the average and maximum of a number of series, add another loop "around" it.

How can I read from the same input file multiple times from different points within the file using sentinel values (-1) in c++?

I have a task where I have to read different sections of an input file(.txt) of integers in c++. The file contains an unknown number of positive integers, each separated by white-space with several sentinel values of -1 placed randomly in the list to "break-up" the list into sections and another -1 at the end of the file.
Here is a sample of my input file(.txt):
3 54 35 4 9 16 -1 14 57 32 4 6 8 41 2 -1 5 6 54 21 3 -1
Here is what I've attempted so far:
int data[20],
index = 0;
ifstream fin;
fin.open("data_file.txt");
while (index < 20 && data[index] != -1 && fin >> data[index])
{
cout << data[index] << endl;
index++;
}
I can't get this to read past the first SV even if I repeat this while loop. It always just starts at the beginning of the file.
How do I read again STARTING AFTER the first SV to the second SV? The only methods I know involve reading a file from beginning to end. How do I read seperate sections?
Thanks in advance for any help,
Cheers
It sounds like you just want to group information from the file. I will not provide code since you didn't, but I may help you with the logic:
Create a file object, 2d vector, and a string
Read from the file object to the string
if the value is equal to "-1", then add a new row. Else, add a new column
The result will be a 2d vector with the rows being each group, and the columns being each positive number in that group.

Reading a file of number into an array while skipping first two values every 1026 entries

I am trying to read in a text file of numbers in which there are 2 values in the beginning that I do not care about, followed by 1024 values that I do care about. The file has approximately 100000 entries that I need to do a calculation on every 1024 of them. The format is something like
1
1025
3000
3572
3579
4023
3593
2930
.
.
.
1
1025
.
.
.
So basically the 1 and the 1025 are header values describing the data set which I need to ignore, then I need to read every value after those header values into an array so I can then run calculations on the values in the array. I was thinking of using while(!file.eof()) but I can not think of how to have the code skip those two numbers while it reads through the 100000 entries. I am pretty new to c++, I usually use GUI's to do my data analysis, but I am on a project that is requiring me to us C++, so I'm really out of my comfort zone here. I appreciate any help I can get.
There are a lot of ways you can do it. The most straight forward example I could think of was:
#include <iostream>
#include <string>
int main()
{
int i = 0;
std::string s;
while( std::cin >> s )
{
if( i++ < 2 ) continue;
std::cout << s;
if( i == 1024 ) i = 0;
}
}

Parsing columns into arrays, while discriminating whats in the rows

I'm trying to parse a text file that is outputted like the example below, my example has limited entries but my actual one has over 15000 lines, so i can't read these in individually:
ID IC TIME
15:23:43.867 /g/mydata/dataoutputfile.txt identifier
0003 1233 abcd
0043 eb54 abf3
000f 0bb4 ac24
000a a325 ac75
0023 0043 ac91
15:23:44.000 /g/mydata/dataoutputfile.txt identifier
0003 1233 abcd
0043 eb54 abf3
000f 0bb4 ac24
000a a325 ac75
0023 0043 ac91
Is kind of the output I have. The time column resets every so often.
What I am doing now is making 2 additional columns in addition to the 3 i have in my example. The first column is the conversion of the ID column, into a translation into an understandable message. The second additional column will calculate the difference between each time code, except when the time code resets.
My logic is, is to read each column into an array so I can perform the necessary translations and operations.
I am focusing on getting the timecode differential first, as I think getting the translation will be a bit simpler.
The problem I'm having is getting the entries read into their matrices:
my code looks a bit like this:
while(readOK && getline(myfile,line))
{
stringstream ss(line);
string ident,IC,timehex,time,filelocation;
string junk1,junk2;
int ID[count];
int timecode[count2];
int idx=0;
if(line.find("ID") !=string::npos)
{
readOK=ss>>ident>>IC>>timehex;
myfile2<<ident<<"\t\t"<<IC<<"\t\t"<<timehex<<"\t\t"<<"ID Decoded"<<"\t\t"<<"DT"<<endl;
myfile3<<"headers read"<<endl
}
else if(line.find("identifier") != string::npos)
{
readOK=ss>>time>>filelocation;
myfile3<<"time and location read";
myfile2<<time<<"\t\t"<<filelocation<<endl;
}
else //this is for the hex code lines
{
readOK=ss>>hex>>ID[idx]>>IC>>timecode[idx];
if (readOK)
{
myfile2<<setw(4)<<setfill('0')<<hex<<ID[1000]<<"\t\t"<<IC<<"\t\t"<<timecode[1000]<<endl;
myfile3<<"success reading info into arrays"<<endl;
}
else
myfile3<<"error reading hex codes"<<endl;
}
idx++;
}
Although this code doesn't work correctly. I can't just read in every line quite the same because of the intervening time and file location entries that are inserted to help keep track of when I am looking at in my code.
My gut is telling me that I'm calling the matrix entries too early and they haven't been filled yet, because if I cout number 1000, I get a 0 (i have well over 15000 lines in my input file and I have the boundaries of my arrays set dynamically in another part of my program).
I can't seem to figure out how to get the entries assigned correctly as I am having some inheritance issues with the count variable resetting to 0 every time through the loop.
Define int idx outside of the scope of the while loop (before the while). As it is now, each time through the loop it will be reset.