Opening/Writing to multiple files - c++

I am trying to write some code that is going to output a speed and time values to multiple files, specifically 4 different files which represents 4 different objects moving.
I know that to open and write to 1 file I can do the following:
#include <iostream>
#include <fstream>
std::ofstream outfile ("test.txt");
outfile << "my text here!" << std::endl;
outfile.close();
However I need to adapt this such that it would work in a switch statement as follows:
for (int i = 0; i < numRobots; i++){
switch (i){
case 0:
if (r1_stop == 1) r1Speed = 0;
else //Update speed
r1File << r1Speed << " " << time << endl;
case 1:
if (r2_stop == 1) r2Speed = 0;
else //Update speed
r2File << r2Speed << " " << time << endl;
case 2:
if (r3_stop == 1) r3Speed = 0;
else //Update speed
r3File << r3Speed << " " << time << endl;
case 3:
if (r4_stop == 1) r4Speed = 0;
else //Update speed
r4File << r4Speed << " " << time << endl;
}
}
Where r1File, r2File, r3File, and r4File are the files containing the speed and time for the respective objects. Im not quite sure how to implement this type where I have multiple files open or do i have to keep opening and closing the files? Im worried about overwriting existing data in the file if that is the case unless it knows to not start from the beginning of the file when opening it again.
Any help is appreciated, thanks

By default std::ofstream overwrites instead of appends, so from the moment you open the file it will be streaming bytes in that overwrite whatever was there before it was opened.
*fstream variants keep a file open until the stream object is destroyed. In other words a file being open is tied to the lifetime of the *fstream object representing it. Once destroyed, the file is immediately closed. This concept is known as RAII. In your case the stream objects are global, and thus are destroyed after main() ends, right before the application terminates.
If writing to the files isn't time critical, your implementation is good enough. On the other hand if you need more accurate measurements, consider writing each of your outputs to an intermediate buffer such as std::stringstream, then stream that data to your files after you're done taking measurements.
However, if you don't need the data from previous application runs, don't bother using files and instead just write to memory using a std::stringstream.

Related

How to I update a pre-existing leaderboard text file, making sure it's sorted?

First of I'd like to thank you all in advance for taking your time reading and helping me with my problem. I'm in no way shape or form an expert at c++, I'm not even good. I started programming in c++ 2 months ago and I find it quite harder than python, for a second experience with programming languages.
So I'm making this game for my programming class and I have to have a leaderboard text file with all the winners of a certain level. I set it up so the file always has the same format for time, name like this.
I've been trying to figure out how to sort the leaderboard entries by time and then by name. I thought of reading the file from line 3 and beyond but that doesn't seem to work. I moved on to what seems a better way of doing it which is to read the whole leaderboard discarding the first 2 lines, store it line by line on a vector, sorting the vector then and wiping the file by opening it in trunc mode but for some reason the file doesn't get wiped, it just keeps on adding more and more entries. I wan't it to add the sorted lines (vector) to the leaderboard one by one up until 10 entries are hit. Can someone help me? Here's a code sniped with the function I'm using to update the leaderboard
// Function to check if MAZE_XX_WINNERS.txt exists, if not creates it
void makeLeaderboard(string maze_name, string formated_time){
string winner_name, filename = maze_name.substr(0,7) +"_WINNERS.txt";
while(true){
// If MAZE_XX_WINNERS.txt file exists
if(ifstream(filename)){
// Open MAZE_XX_WINNERS.txt file in append mode
fstream leaderboard(filename, fstream::app);
// Ask for player name
cout << "Type your name (max 15 characters): ";
getline(cin, winner_name);
// If name is valid
if(isValidName(winner_name) && winner_name.length() <= 15){
string line;
vector<string> lb_entries;
int n_line = 0;
// Append to the end of the file
leaderboard << formated_time << " - " << winner_name << endl;
// Store all leaderboard entries in a vector
while(!leaderboard.eof()){
if(n_line >= 2){
getline(leaderboard, line);
lb_entries.push_back(line);
}
n_line++;
}
leaderboard.close();
//Everything works up until here, past here it doesn't do anything I want it to do
// Sort the leaderboard entries first by time, then by name
sort(lb_entries.begin(), lb_entries.end());
// Check if leaderboard has more than 10 entries to delete those past the limit
if(lb_entries.size() > 10){
// Truncates the vector from the 10th position forward
lb_entries.erase(lb_entries.begin()+9, lb_entries.end());
}
// Reopens the file in truncation mode to delete pre-existing leaderboard
leaderboard.open(filename, fstream::trunc);
// Format the file to have a table like shape
leaderboard << "| TIME - NAME |" << endl;
leaderboard << "------------------------------" << endl;
// Updates leaderboard
for(string entry : lb_entries){
leaderboard << entry << endl;
}
leaderboard.close();
break;
}
// If name not valid
else if(isValidName(winner_name) && winner_name.length() > 15){
cerr << endl << "Name has more than 15 characters! Please retry." << endl << endl;
}
else{
cerr << endl << "Not a valid name input!" << endl << endl;
}
}
// If file doesn't exist
else{
// Attempt to create the file
cout << "Creating leaderboard..." << endl;
ofstream leaderboard(filename);
// Check if file was created
if(!leaderboard){
cerr << "File could not be created" << endl;
}
else{
// Format the file to have a table like shape
leaderboard << "| TIME - NAME |" << endl;
leaderboard << "------------------------------" << endl;
leaderboard.close();
}
}
}
}
You need to break your problem down. What I would do is create a class that represents the LeaderBoards. It would actually consist of two classes. You could do one as an inner class of the others, but let's keep them separate:
class Leader {
public:
std::string time;
std::string name;
};
class LeaderBoard {
public:
std::vector<Leader> leaders;
void readFromFile(std::string fName);
void sort();
void writeToFile(std::string fName);
};
At that point, you need to implement three functions. None of them are very long.
void LeaderBoard::readFromFile(std::string fName) {
std::ifstream file(fName);
std::string line;
// skip the header
file.getline(line);
file.getline(line);
// Read the rest of the file.
while (file.getline(line)) {
// You'll need to parse the line into its parts
Leader leader(from the parts);
leaders.push_back(leader);
}
}
Yeah, I left some magic for you.
The write method would be very simple and just use an ofstream instead of an ifstream.
The sort method -- you can do a google for "c++ sort vector of objects" and get LOTS of examples.
In general, ALL programming can be broken down into smaller steps. If you're getting overwhelmed, break it down. This is one of the reasons you use an object-oriented language. If you don't know how to do something, create a class for it, then put methods in it for the smaller steps.
Then just figure out how to do small parts at a time. First: get data. Then print it out so you're sure you've got what you need.
If your code is more than about a screen or so, you're doing too much in one method. That's not an absolute, but at your level of coding, it's definitely true.
Small, tight methods. Small, tight methods are easier to write. Then string them together.
In this case:
Read the data
Sort the data
Write the data.
Each of these is easy to test individually.

How can I test if either of two files exist?

I am new to C++,
I am using a while true to find a Pass.txt file.
Once it finds it the while loop breaks and prints "Pass.txt file FOUND"
This works fine
I then would like to see if it can find Pass.txt OR Fail.txt with a while true
something like this ifstream ifile("Pass.txt || Fail.txt");
This does not work:
cout << " ------------------------------------------\n";
cout << " Searching for Pass or Fail file \n";
cout << " ------------------------------------------\n";
cout << "\n\n";
while (true)
{
ifstream ifile("Pass.txt");
//ifstream ifile("Pass.txt ||Fail.txt");
if (ifile)
{
// The file exists, and is open for input
break;
}
}
cout << "\n\n";
cout << " ------------------------------------------\n";
cout << " Pass.txt FOUND \n";
cout << " ------------------------------------------\n";
cout << " ------------------------------------------\n";
Your code fails because you’re not testing for the existence of two files. You are testing whether the one file with the weird name Pass.txt || Fail.txt exists. You need two make two separate existence checks, one for each separate file.
To do this, don’t open the file — use std::filesystem::exists:
#include <filesystem>
#include <thread>
// …
int main() {
using namespace std::chrono_literals;
namespace fs = std::filesystem;
while (not fs::exists("Pass.txt")) {
std::this_thread::sleep_for(500ms); // Be gentle to the system.
}
while (not (fs::exists("Pass.txt") or fs::exists("Fail.txt"))) {
std::this_thread::sleep_for(500ms); // Be gentle to the system.
}
}
(You can also write ! instead of not, and || instead of or; but I prefer using these descriptive terms in my own code.)
We use std::this_thread::sleep to avoid hammering the system with a busy loop, which will spin up your CPU like crazy even though your code isn’t doing any real work.
However, note that merely testing for a file’s existence isn’t very useful. And your second loop will immediately terminate, since "Pass.txt" exists after the first loop (unless, by a freak coincidence, it was deleted in the nanoseconds between the two loops).

Decreasing output fstream

I'm working with an pressure sensitive LED floor, which outputs its sensors in a 2D array. I want to record this data and send it to a CSV file. However with my current code I gather 300.000 KB per second.
void DemoProjectApp::recorddata()
{
std::ofstream myfile;
myfile.open("datafloor.csv");
while(pressed = true)
{
for (int i = 0; i < ProjectSettings::NR_OF_TILES; i++)
{
myfile << "Tile" << i << ",";
myfile << tileIndexToSensorValues[i][0] << ",";
myfile << tileIndexToSensorValues[i][1] << ",";
myfile << tileIndexToSensorValues[i][2] << ",";
myfile << tileIndexToSensorValues[i][3] << "\n";
myfile.flush();
}
}
if(pressed = false)
{
myfile.close();
}
}
Pressed is a boolean changing true or false to start and stop recording.
Does anyone have a solution to decrease the output, but still get the relevant data?
Thanks in advance!
What you're writing there is effectively a series of images. Aka a video file. A video encoded in a very inefficient coding scheme. I guess the pressure field does not have steep changes, so my first approach would be encoding it with a video codec (MPEG4, h264, or such).
Okay, feeling really stupid but the answer was adding == at the pressed booleans.
Totally overlooked it.

Writing/reading large vectors of data to binary file in c++

I have a c++ program that computes populations within a given radius by reading gridded population data from an ascii file into a large 8640x3432-element vector of doubles. Reading the ascii data into the vector takes ~30 seconds (looping over each column and each row), while the rest of the program only takes a few seconds. I was asked to speed up this process by writing the population data to a binary file, which would supposedly read in faster.
The ascii data file has a few header rows that give some data specs like the number of columns and rows, followed by population data for each grid cell, which is formatted as 3432 rows of 8640 numbers, separated by spaces. The population data numbers are mixed formats and can be just 0, a decimal value (0.000685648), or a value in scientific notation (2.687768e-05).
I found a few examples of reading/writing structs containing vectors to binary, and tried to implement something similar, but am running into problems. When I both write and read the vector to/from the binary file in the same program, it seems to work and gives me all the correct values, but then it ends with either a "segment fault: 11" or a memory allocation error that a "pointer being freed was not allocated". And if I try to just read the data in from the previously written binary file (without re-writing it in the same program run), then it gives me the header variables just fine but gives me a segfault before giving me the vector data.
Any advice on what I might have done wrong, or on a better way to do this would be greatly appreciated! I am compiling and running on a mac, and I don't have boost or other non-standard libraries at present. (Note: I am extremely new at coding and am having to learn by jumping in the deep end, so I may be missing a lot of basic concepts and terminology -- sorry!).
Here is the code I came up with:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <fstream>
# include <iostream>
# include <vector>
# include <string.h>
using namespace std;
//Define struct for population file data and initialize one struct variable for reading in ascii (A) and one for reading in binary (B)
struct popFileData
{
int nRows, nCol;
vector< vector<double> > popCount; //this will end up having 3432x8640 elements
} popDataA, popDataB;
int main() {
string gridFname = "sample";
double dum;
vector<double> tempVector;
//open ascii population grid file to stream
ifstream gridFile;
gridFile.open(gridFname + ".asc");
int i = 0, j = 0;
if (gridFile.is_open())
{
//read in header data from file
string fileLine;
gridFile >> fileLine >> popDataA.nCol;
gridFile >> fileLine >> popDataA.nRows;
popDataA.popCount.clear();
//read in vector data, point-by-point
for (i = 0; i < popDataA.nRows; i++)
{
tempVector.clear();
for (j = 0; j<popDataA.nCol; j++)
{
gridFile >> dum;
tempVector.push_back(dum);
}
popDataA.popCount.push_back(tempVector);
}
//close ascii grid file
gridFile.close();
}
else
{
cout << "Population file read failed!" << endl;
}
//create/open binary file
ofstream ofs(gridFname + ".bin", ios::trunc | ios::binary);
if (ofs.is_open())
{
//write struct to binary file then close binary file
ofs.write((char *)&popDataA, sizeof(popDataA));
ofs.close();
}
else cout << "error writing to binary file" << endl;
//read data from binary file into popDataB struct
ifstream ifs(gridFname + ".bin", ios::binary);
if (ifs.is_open())
{
ifs.read((char *)&popDataB, sizeof(popDataB));
ifs.close();
}
else cout << "error reading from binary file" << endl;
//compare results of reading in from the ascii file and reading in from the binary file
cout << "File Header Values:\n";
cout << "Columns (ascii vs binary): " << popDataA.nCol << " vs. " << popDataB.nCol << endl;
cout << "Rows (ascii vs binary):" << popDataA.nRows << " vs." << popDataB.nRows << endl;
cout << "Spot Check Vector Values: " << endl;
cout << "Index 0,0: " << popDataA.popCount[0][0] << " vs. " << popDataB.popCount[0][0] << endl;
cout << "Index 3431,8639: " << popDataA.popCount[3431][8639] << " vs. " << popDataB.popCount[3431][8639] << endl;
cout << "Index 1600,4320: " << popDataA.popCount[1600][4320] << " vs. " << popDataB.popCount[1600][4320] << endl;
return 0;
}
Here is the output when I both write and read the binary file in the same run:
File Header Values:
Columns (ascii vs binary): 8640 vs. 8640
Rows (ascii vs binary):3432 vs.3432
Spot Check Vector Values:
Index 0,0: 0 vs. 0
Index 3431,8639: 0 vs. 0
Index 1600,4320: 25.2184 vs. 25.2184
a.out(11402,0x7fff77c25310) malloc: *** error for object 0x7fde9821c000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
And here is the output I get if I just try to read from the pre-existing binary file:
File Header Values:
Columns (binary): 8640
Rows (binary):3432
Spot Check Vector Values:
Segmentation fault: 11
Thanks in advance for any help!
When you write popDataA to the file, you are writing the binary representation of the vector of vectors. However this really is quite a small object, consisting of a pointer to the actual data (itself a series of vectors, in this case) and some size information.
When it's read back in to popDataB, it kinda works! But only because the raw pointer that was in popDataA is now in popDataB, and it points to the same stuff in memory. Things go crazy at the end, because when the memory for the vectors is freed, the code tries to free the data referenced by popDataA twice (once for popDataA, and once again for popDataB.)
The short version is, it's not a reasonable thing to write a vector to a file in this fashion.
So what to do? The best approach is to first decide on your data representation. It will, like the ASCII format, specify what value gets written where, and will include information about the matrix size, so that you know how large a vector you will need to allocate when reading them in.
In semi-pseudo code, writing will look something like:
int nrow=...;
int ncol=...;
ofs.write((char *)&nrow,sizeof(nrow));
ofs.write((char *)&ncol,sizeof(ncol));
for (int i=0;i<nrow;++i) {
for (int j=0;j<ncol;++j) {
double val=data[i][j];
ofs.write((char *)&val,sizeof(val));
}
}
And reading will be the reverse:
ifs.read((char *)&nrow,sizeof(nrow));
ifs.read((char *)&ncol,sizeof(ncol));
// allocate data-structure of size nrow x ncol
// ...
for (int i=0;i<nrow;++i) {
for (int j=0;j<ncol;++j) {
double val;
ifs.read((char *)&val,sizeof(val));
data[i][j]=val;
}
}
All that said though, you should consider not writing things into a binary file like this. These sorts of ad hoc binary formats tend to live on, long past their anticipated utility, and tend to suffer from:
Lack of documentation
Lack of extensibility
Format changes without versioning information
Issues when using saved data across different machines, including endianness problems, different default sizes for integers, etc.
Instead, I would strongly recommend using a third-party library. For scientific data, HDF5 and netcdf4 are good choices which address all of the above issues for you, and come with tools that can inspect the data without knowing anything about your particular program.
Lighter-weight options include the Boost serialization library and Google's protocol buffers, but these address only some of the issues listed above.

Reading data from a .csv and into arrays in c++

I did some searching on this site and on google as well. But i couldnt understand a lot of code i seen and im hoping to find more direct help from here. Im only 2 semesters in for c++ and i have a side project id like to do for my boss.
He generates a csv file for call logs and i want to be able to retrieve certain lines from the log and be able to calculate and display data.
Im not sure of the exact questions i need to ask but heres my code where i tried to start getting data but ran into problems (my programming knowledge is fairly limited due to lack of time and experience :)
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
int main()
{
//opens the csv file.
ifstream gwfile;
gwfile.open("log.csv");
if(!gwfile) { // file couldn't be opened
cout << "FAILED: file could not be opened" << endl << "Press enter to close.";
cin.get();
return 0;
}else
cout << "SUCCESSFULLY opened file!\n";
cout << "-------------------------------------------------------------------------------\n\n\n";
long int SIZE = 0;
char data[SIZE];
cout << "This is data SIZE:" << data[SIZE] << endl;
//in the csv im trying to only read lines that start with the voice as those are only valid data we need.
//also i would like to display the headings in teh very first line
while( !gwfile.eof() ){
//This is where im trying to only accept the lines starting with "Voice"
//if(data[SIZE] == "Voice"){
for( int i=0; i!=","; i++){
cout << "This is i: " << i << endl; //testing purposes.
}
//}
// getline(gwfile, data, '');
// cout << data[0];
}
return 0;
}
Let’s begin with the obvious
long int SIZE = 0;
char data[SIZE];
cout << "This is data SIZE:" << data[SIZE] << endl;
You are creating an array of size 0, then reaching for its first member: data[0]. This cannot work. Give your array a size that is large enough to handle the data you want to treat, or use a dynamicly resizable container (such as std::vector) to deal with it.