So I'm having trouble understanding files in C++ - c++

I just started learning files and I understand how to set it up and get it to work. I have to write this program where I have to allow the user to enter some information and have the user also update and adjust any data, using binary.
So I can write up until the point where the user can write to and read from the file. But I don't know how to let the user adjust data or add data.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class client {
public:
string name;
int balance;
string id;
};
int main()
{
int ans;
int x;
string nameIn;
string adjName;
client client1;
ofstream out("client1.dat", ios::binary);
cout << "\nDo you want to add information or update info" << endl;
cin >> ans;
if (ans == 1)
{
cout << "\nPlease enter the name of your client" << endl;
cin >> nameIn;
x = nameIn.length();
if (x <= 10)
{
for (int i; i < 10; i++)
{
adjName[i] = nameIn[i];
}
}
else
{
for (int i = x; i < 10; i++)
{
adjName[i] = ' ';
}
}
client1.name = adjName;
cout << "\nPlease enter the balance of your client" << endl;
cin >> client1.balance;
cout << "\nPlease enter the id of your client" << endl;
cin >> client1.id;
cout << "\nThe name of your client is " << endl << client1.name
<< endl << "\nThe balance of your client is " << endl
<< client1.balance << endl << "\nThe id of your client is "
<< endl << client1.id;
out.write(reinterpret_cast<const char*> (&client1), sizeof(client));
}
/*
else if (ans == 2)
{
string answer, newName,line;
cout << "\nWhat name do you want to update? " << endl;
cin >> answer;
cout << "\nWhat is the new name?" << endl;
cin >> newName;
if (out)
}
*/
system("pause");
return 0;
}
so the name needs to be only 10 characters long, so that we can adjust/update it. It compiles and runs, but every time the compiler gets to the part where it checks the name length, it freaks out and says "debug assertion failed"
string subscript out of range.
Also a thing about this code-- if i run it without the bits where you adjust the name to a certain array length, the program runs, and stores everything nicely. But when I try to read back the .dat, it reads it back but exits with an access violation, forcing me to manually stop the debugging. What am I doing wrong?
this is the code for reading the file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class client {
public:
string name;
int balance;
string id;
};
int main()
{
client client1;
char ans;
cout << "\nDo you want to view the information about your client?"
<< endl;
cin >> ans;
ifstream in("client1.dat", ios::binary);
if (ans == 'y' || ans == 'Y')
{
in.read(reinterpret_cast<char*> (&client1), sizeof(client));
cout << "The name is " << endl << client1.name << endl
<< "The balance is " << endl << client1.balance << endl
<< "The id is " << endl << client1.id << endl;
}
system("pause");
return 0;
}

As for the 1st part:
for (int i; i < 10; i++)
// ^
misses to initialize i to zero. Also what if the input was smaller than 10 characters? You're going to access the std::string out of bounds. You should replace the if/else and loops with simply
adjName = nameIn;
while(adjName.length() <= 10) {
adjName += ' ';
}
to get rid of the debug assertion.
For the 2nd part of the question, as already mentioned in the comments you cannot do this with a structure containing classes like std::string.
The reinterpret_cast<char*> (&client1) just obfuscates that std::string uses a pointer to the dynamically allocated character data internally, and that cannot be restored meaningfully when reading the stored data back later (hence the access violation you get).
A viable way might be to use something like
struct client {
char name[11];
int balance;
char id[5];
};
As I guess you need to do this for a homework exercise, and for this purpose that would probably be sufficient.
But you quickly can see the drawbacks, that the character data needs to be fixed in size and you cannot have arbitrary length strings. I never would use such for production ready code.
Another pitfall (as also mentioned) is, that int isn't represented in the same way (order of bytes used, i.e. endianess) in the same way for different CPU architectures. So the binary file can't be used portably with different computers.
The simplest solution is not to use a binary file, but a text formatted file and overload the std::ostream& operator<<(std::ostream&, const client&) and std::istream& operator>>(std::istream&, client&) output/input operators.
Or use some 3rd party library like boost::serialization or google protocol buffers, that supports de-/serialization to binary files.

Related

how to store struct in a text file in c++

I want to store the elements of struct into a text file. I have multiple inputs and this is what I have done, however, I can only store the latest inputs but not all the input. Thanks in advance for the help! Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
struct ProcessRecords {
string ID;
int arrival;
int wait;
int burst;
void putToFile() {
ofstream input;
input.open ("process.txt");
input << ID << "\t" << arrival << "\t" << wait << "\t" << burst << endl;
input.close();
}
};
int main() {
int numProcess;
int algo;
cout << "\n\t\t=== CPU SCHEDULING ALGORITHMS ===\n";
cout << "\n\t\tEnter number of processes: ";
cin >> numProcess;
ProcessRecords process[numProcess];
string processID[numProcess];
int arrTime[numProcess];
int waitTime[numProcess];
int burstTime[numProcess];
cout << endl << endl;
for (int i = 0; i < numProcess; i++) {
cout << "\n\tEnter process ID for Process " << i+1 << ":\t ";
cin >> processID[i];
process[i].ID = processID[i];
cout << "\n\t\tEnter arrival time for " << processID[i] << ":\t ";
cin >> arrTime[i];
process[i].arrival = arrTime[i];
cout << "\n\t\tEnter waiting time for " << processID[i] << ":\t ";
cin >> waitTime[i];
process[i].wait = waitTime[i];
cout << "\n\t\tEnter burst time for " << processID[i] << ":\t ";
cin >> burstTime[i];
process[i].burst = burstTime[i];
process[i].putToFile();
}
return 0;
}
Here is my sample output:
First In C++(by C++ i mean standard C++ and not extensions), the size of an array must be a compile time constant. So you cannot write code like:
int n = 10;
int arr[n]; //incorrect
Correct way to write this would be:
const int n = 10;
int arr[n]; //correct
For the same reason the following statement is incorrect in your code :
int arrTime[numProcess]; //incorrect because size of array must be fixed and decide at compile time
Second you should append to the text file instead of overwriting it. For opening the file in append mode you can use:
input.open("process.txt", ofstream::app);
You can check here that the program works(append) as you desire, using input.open("process.txt", ofstream::app); . Also do note what i said about the size of array being compile time constant. I have not changed it in the link i have given. You can use std::vector for variable size container.

String doesn't want to store a 2700 character word

I'm trying to make a program that prints all the numbers from 100-999. After that you get to choose how many numbers you want to find. Then you type the number's position and it will be outputed.
There is one problem. The string, named str, stops storing at the number 954.
Here's the code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
//Prints to myFile the numbers from 100 to 999 without a space in between. Like this: 100101102...999
ofstream myFile("numere.txt");
for(int i = 100; i <= 999; i++)
myFile << i;
//Makes the string str to store the line: 100101102103...999. But only stores until 954 (100101102..954)
ifstream myFileRead("numere.txt");
string str;
while(getline(myFileRead, str))
cout << str << endl;
//Ouputs the lenght that should be 2700 but is instead 2565
cout << endl;
cout << "String legth: " << str.size() << endl;
cout << endl;
int n, k;
cout << "Enter how many numbers do you want to find: ";
cin >> n;
for(int i = 1; i <= n; i++){
cout << "Enter number position(it starts from 0) : ";
cin >> k;
cout << "Here's the number on position " << k << ": " << str.at(k);
cout << endl;
}
system("pause>0");
}
Thanks for your attention. I’m looking forward to your reply.
C++ streams are buffered. When you use << to write to a file it is not immediately written to the file.
Try to close or flush the ofstream before you read from it:
myFile.close(); // or...
myFile.flush();
For more details I refer you to flush() and close().
PS: Actually it is rather rare that you need to close a fstream explicitly. You wouldn't need to do it when you used seperate functions for writing and reading:
void write_to_file() {
std::ofstream myFile("numere.txt");
//...
}
void read_from_file() {
std::istream myFile("numere.txt");
//...
}
Because the destructor of ofstream already closes the file.

C++ not waiting for an input

so I have been learning C++ and was working on a monkey see monkey do program and i managed to get the first input working but the second input it just skips straight over it, i have no clue why and any help would be apreciated.
#include <iostream>
#include <string>
using namespace std;
char monkey_1;
char monkey_2;
int msmd()
{
cout << "monkey one:"; cout << endl;
cin >> monkey_1;
system("cls");
cout << "monkey two:"; cout << endl;
cin.clear();
cin >> monkey_2;
cout << "waiting"; cout << endl;
if (monkey_1 == monkey_2)
{
cout << "both monkeys are happy."; cout << endl;
}
else
{
cout << "the monkeys are upest."; cout << endl;
}
return 0;
}
void main()
{
msmd();
}
Do you intent to only get a single character from the input as the monkeys are of type char? If not, change them to string, otherwise it will only assign a single character per cin.
If you want to input a sentence, cin also splits on spaces, so if you enter "something else", the first cin will assign something to monkey_1, and the second cin will automatically assign else to monkey_2.
To get around this you can use getLine(cin,monkey_x).
There are two point needs to be modified.
char monkey_1 and mokeny_2 should be declared as string for get more than 1 character.
void main need to be changed as int main.

cannot understand the if command

I'm trying to write a program, which shows a custom message in the console when I type load. But I can't seem to get it to work. :/
#include "stdafx.h"
#include "iostream"
#include "string"
using namespace std;
int main()
{
int commands();
string text;
cout << "Write your registered e-mail to continue...\n";
cin >> text;
string input;
cout << "\n";
cout << "Welcome " << text << endl;
cout << "\n";
cout << "www.steamcommunity.com/id/thetraderdazz> ";
cin >> input;
if (input = load);
cout << "loading...";
system("pause");
return 0;
}
It also gives me the following error:
identifier "load" is undefined.
if (input = load);
There are three mistakes with this line. The first is that you used the assignment operator = instead of comparison operator ==. The former assigns, the latter compares.
The second mistake is that you placed a semicolon after the parenthesis, indicating an empty body. Your compiler should have given you a warning about this.
Finally, there is no variable load. You mean to compare to string literal "load".
Fix to
if (input == "load")
cout << "loading...\n";
You probably intended the following
if (input == "load") {
cout << "loading...";
}

Storing user input data in a dynamic array of structures/ displaying said data. C++

So for my project I have to...
Create a structure using the format: struct PERSON{string name; int age; float gpa;};
Ask the user to enter the number of records they would like to enter.
Create a dynamic array p of PERSON type to be able to hold all records in part 2
Read data into array p in part 2
Display array p
However, whenever I run my program and begin to enter my data it won't let me enter more than one set of data and the output seems to make little sense.
Here is my code:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct PERSON
{
string name;
int age;
float gpa;
};
PERSON *p;
int main()
{
int count;
cout << "Please enter the number of records you wish to enter: ";
cin >> count;
p = new (nothrow) PERSON[count];
if (p == nullptr)
cout << "Error: memory could not be allocated";
for (int i = 0; i < count; i++)
{
cout << "Enter name, age, and gpa: ";
getline(cin, p[i].name);
cin >> p[i].age;
cin >> p[i].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw(8) << "NAME" << setw(7) << "AGE" << setw(7) << "GPA" << endl;
cout << "--------------------------" << endl;
for (int i = 0; i < count; i++)
{
cout << p[i].name << " " << p[i].age << " " << p[i].gpa << endl;
}
return 0;
}
Now, this format seemed to work just fine when I was copying data from a file into a dynamic array of structures, but I can't seem to get it to work now that I'm dealing with user input.
Here is what a trial run of the program produces:
Please enter the number of records you wish to enter: 3
Enter name, age, and gpa: Robert 21 2.1 // This is the info I tested
Enter name, age, and gpa:
Enter name, age, and gpa:
This is the content of array p:
NAME AGE GPA
--------------------------
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
Press any key to continue . . .
I've probably gone over the code two dozen times now and have been searching for an answer for quite a while. Any advice/analysis/comments would be greatly appreciated.
So #AndyG suggested that I change the getline(cin, p[i].name) to cin >> p[i].name and that seemed to clear things up. Thank you!
Sorry to trouble everyone over such a silly muck-up.
The Problem
Your getline(cin, p[i].name) is actually grabbing all your input on the same line, and your later cin statements are going to be mismatched.
std::getline should be reserved for getting an entire line into a character buffer or std::string, i.e., it's not always the appropriate tool for reading in a string.
The Solution
Instead, you simply need to replace getline(cin, p[i].name) with cin >> p[i].name.
Live Demo
Other minor changes
Remove the nullptr check:
if (p == nullptr)
cout << "Error: memory could not be allocated";
Because in C++ you will get an exception thrown if the new operator fails to allocate. Like bku_drytt mentioned, you can wrap this in a try catch block instead, although I'd personally just remove it altogether.
#include <new>
// ...
Person* p = null;
try
{
p = new Person[count];
} catch(std::bad_alloc& ex)
{
cerr << "Unable to allocate memory for Person! Error msg: " << ex.what() << "\n";
return 1;
}
Finally, don't forget to delete[] the memory you allocated for p:
// ...
delete[] p;
return 0;
} //end of int main()
Even though your OS will most definitely clean that memory up for you, you should still ensure you clean it up manually. Good practice at least. Alternatively, you can use a std::array or a std::vector instead, which will still give you random access just like the c-style array of PERSON you already have.
The std::getline() function is "misbehaving" in relation to your intentions because there is (I think) a new line character left in the stream input queue.
You can discard such characters by using the following command cin.sync().
Why does this happen? Because operator>> does not read whitespaces, but std::getline() does, so when the cin >> count; statement is executed and you press enter, the new line character (from pressing enter) is left in the stream input queue, which is automatically read by std::getline() upon execution.
There are other functions that can accomplish this and perhaps someone more informed can tell us which is the best choice.
Here is a fixed version:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct Person
{
string name;
int age;
float gpa;
};
int main()
{
cout << "Please enter the number of records you wish to enter: ";
int count;
cin >> count;
Person* p = new Person[ count ];
for ( int i = 0; i < count; i++ )
{
cin.sync(); // added this to discard unused characters from input queue
cout << "Enter name: ";
getline( cin, p[ i ].name );
cout << "Enter age: ";
cin >> p[ i ].age;
cout << "Enter gpa: ";
cin >> p[ i ].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw( 8 ) << "NAME" << setw( 7 ) << "AGE" << setw( 7 ) << "GPA" << endl;
cout << "--------------------------" << endl;
for ( int i = 0; i < count; i++ )
{
cout << p[ i ].name << " " << p[ i ].age << " " << p[ i ].gpa << endl;
}
return 0;
}
Notes:
You do not need to check if Person* p is nullptr because the new operator will throw a std::bad_alloc exception if it fails to allocate enough memory. Consider wrapping that memory allocation in a try catch block if you really want to.