I am working on a program that reads data from a text file called "phonebook.txt" and displays it in terminal. It ask how many contacts the user wants to add. The user will input the number of contacts he/she wants and the program will output this along with the old contacts from "phonebook.txt" to "updated-phonebook.txt." Now I chose to use a dynamic array over a vector simply because I wanted to get a feel for how to handle memory allocation.
#include <iostream>
#include <string>
#include <fstream>
#define nullptr 0;
struct Person
{
std::string lName, fName, streetAddr, city, state, zip, phoneNum;
};
int main ()
{
std::ofstream outFile;
std::string dummy;
int amount, total, count = 0;
Person *contact;
//set the pointer to 0 so its not pointing
//to anything else in memory
contact = nullptr;
std::ifstream inFile("/home/isemanthaj/My_Programs/Class_Programs/Assignment_3/phonebook/phonebook.txt",std::ios::in);
if (!inFile)
{
std::cout << "File failed to open" << std::endl;
}
else
outFile.open("updated_phonebook.txt", std::ios::out);
std::cout << "How many contacts do you want to add?" << std::endl;
std::cin >> amount;
contact = new Person[amount];
std::cout << "Contacts in the previous book \n" << std::endl;
std::cin.ignore();
while (inFile)
{
//dummy stores the contact number which is the first line
// in the read file. I don't want to carry the number over
// to the other updated_phonebook.txt file
std::getline(inFile, dummy);
std::getline(inFile, contact[count].lName);
std::getline(inFile, contact[count].fName);
std::getline(inFile, contact[count].streetAddr);
std::getline(inFile, contact[count].city);
std::getline(inFile, contact[count].state);
std::getline(inFile, contact[count].zip);
std::getline(inFile, contact[count].phoneNum);
std::cout << contact[count].lName << std::endl;
std::cout << contact[count].fName << std::endl;
std::cout << contact[count].streetAddr << std::endl;
std::cout << contact[count].city << std::endl;
std::cout << contact[count].state << std::endl;
std::cout << contact[count].zip << std::endl;
std::cout << contact[count].phoneNum << std::endl;
outFile << contact[count].lName << std::endl;
outFile << contact[count].fName << std::endl;
outFile << contact[count].streetAddr << std::endl;
outFile << contact[count].city << std::endl;
outFile << contact[count].state << std::endl;
outFile << contact[count].zip << std::endl;
outFile << contact[count].phoneNum << std::endl;
count++;
}
// I know this is a little wacky here.
// I want the program to display the total amount of contacts
// to the screen
total = amount + count;
for (int index = 0; index < total; index++)
{
std::cout << "Last name: ";
std::getline(std::cin, contact[index].lName);
std::cout << "First name: ";
std::getline(std::cin, contact[index].fName);
std::cout << "Street address: ";
std::getline(std::cin, contact[index].streetAddr);
std::cout << "City: ";
std::getline(std::cin, contact[index].city);
std::cout << "State: ";
std::getline(std::cin, contact[index].state);
std::cout << "Zip code: ";
std::getline(std::cin, contact[index].zip);
std::cout << "Phone number: ";
std::getline(std::cin, contact[index].phoneNum);
std::cout << std::endl;
std::cout << "Contact: " << index + 1 << std::endl;
std::cout << "Last name: " << contact[index].lName << std::endl;
std::cout << "First name: " << contact[index].fName << std::endl;
std::cout << "Street address: " << contact[index].streetAddr << std::endl;
std::cout << "City: " << contact[index].city << std::endl;
std::cout << "State: " << contact[index].state << std::endl;
std::cout << "Zip code: " << contact[index].zip << std::endl;
std::cout << "Phone number: " << contact[index].phoneNum << std::endl;
std::cout << std::endl;
outFile << "Last name: " << contact[index].lName << std::endl;
outFile << "First name: " << contact[index].fName << std::endl;
outFile << "Street address: " << contact[index].streetAddr << std::endl;
outFile << "City: " << contact[index].city << std::endl;
outFile << "State: " << contact[index].state << std::endl;
outFile << "Zip code: " << contact[index].zip << std::endl;
outFile << "Phone number: " << contact[index].phoneNum << std::endl;
}
inFile.close();
outFile.close();
delete [] contact;
return 0;
}
I am trying to store the new contacts the user created and the old contacts from the read file in one dynamic array of structures. Am I getting this segmentation fault because I am using two different indexes "count" and "index" to store the contacts in the same dynamic array?
Your segfault is happening because you are trying to access an out-of-bounds element of contact array. The size of contact is amount, and you are iterating it from 0 to amount + count. Obviously, amount + count >= amount, so sooner or later you will run out of bounds.
I would suggest you to use std::vector instead of a plain array. You will always be aware of it's size and will be able to iterate it safely.
If you want to keep the arrays, you will have to either reallocate contact after copying the contacts from the old file, to make it size equal to total, or make two separate arrays: one to hold the records from the old file with the size of amount elements, and another one for the newly-added contacts with the size of count elements.
As it is mentioned in the comments, your end-of-file check is wrong, this and this questions are relevant.
Related
I'm creating a password system for an encryption program.
For this i moved my main chunk of code inside an if statement, however now, instead of reading the code line by line, it simply prints it all at once. (e.g. when it comes to std::cout << "Enter here: "; std::getline(std::cin, userin); the user cant input anything, it just prints the next line and keeps going.
int main(){
std::string userin;
std::string pass;
int attempts = 3;
std::cout << " ENCRYPTER" << std::endl;
std::cout << " " << std::endl;
while (attempts > 0){
std::cout << "Please Enter the Password" << std::endl;
if(attempts > 1){
std::cout << "You have " << attempts << " attempts remaining." << std::endl;
}
else {
std::cout << "You have " << attempts << " attempt remaining." << std::endl;
}
std::cin >> pass;
std::cout << " " << std::endl;
if(pass == "12345"){
std::cout << greetingFunc() << std::endl;
std::cout << " " << std::endl;
std::cout << "NOTE: Use only LETTERS. No numbers or grammar." << std::endl;
std::cout << " " << std::endl;
std::cout << "Please enter the message you wish to encrypt." << std::endl;
std::cout << "Enter here: "; std::getline(std::cin, userin);
std::cout << " " << std::endl;
std::cout << "Encrypted Message: " << encryptFunc(userin) << std::endl;
std::cout << " " << std::endl;
std::cout << "Highlight it and right click to copy." << std::endl;
system("pause");
} else {
std::cout << "Incorrect password. Please try again." << std::endl;
attempts--;
}
}
return 0;
}
Here is what is received in the console after the password is correctly entered.
Welcome to the Code Encrypter
NOTE: Use only LETTERS. No numbers or grammar.
Please enter the message you wish to encrypt.
Enter here:
Encrypted Message:
Highlight it and right click to copy.
Press any key to continue . . .
See how it just prints everything without running the functions or allowing the user to input their data.
How can i get it to read each line and stop to let the user input the info, and then proceed to call the encryption function? Thanks.
Just add std::getchar(); after std::cin >> pass
The problem is that when you enter pass and click enter, this enter keyboard will be considered as your next input. std::getchar(); receives the effect of enter keyboard. Now you can enter userin.
Note: Use std::getchar() only after you get a string by std::cin.
If you use std::getline(std::cin, pass); instead of std::cin >> pass;, you don't have to worry about the final '\n' because getline() doesn't let this character interrupt the next input.
Using stream operators with getline(cin, variable) is always a problem because some data remains in the input buffer. So you should remove everything still in there then read your input.
I.e.
while(getline(cin, userin))
if (userin != "")
break;
so this should works:
int main(){
std::string userin;
std::string pass;
int attempts = 3;
std::cout << " ENCRYPTER" << std::endl;
std::cout << " " << std::endl;
while (attempts > 0){
std::cout << "Please Enter the Password" << std::endl;
if(attempts > 1){
std::cout << "You have " << attempts << " attempts remaining." << std::endl;
}
else {
std::cout << "You have " << attempts << " attempt remaining." << std::endl;
}
std::cin >> pass;
std::cout << " " << std::endl;
if(pass == "12345"){
std::cout << greetingFunc() << std::endl;
std::cout << " " << std::endl;
std::cout << "NOTE: Use only LETTERS. No numbers or grammar." << std::endl;
std::cout << " " << std::endl;
std::cout << "Please enter the message you wish to encrypt." << std::endl;
std::cout << "Enter here: ";
while (std::getline(std::cin, userin))
if (userin != "")
break;
std::cout << " " << std::endl;
std::cout << "Encrypted Message: " << encryptFunc(userin) << std::endl;
std::cout << " " << std::endl;
std::cout << "Highlight it and right click to copy." << std::endl;
system("pause");
} else {
std::cout << "Incorrect password. Please try again." << std::endl;
attempts--;
}
}
return 0;
}
It looks like you're having issues with the input buffer. This is a very common issue when running command line/terminal programs.
When the cin >> pass is called, the stream buffer may have inputs remaining (namely the '\n' from pressing enter). You can do a few things, but the easiest thing is to call cin.get() after the cin >> pass.
You could also try creating a helper function or parsing the input using a while loop.
When the program enters in the if and then break, empty cout-s are printed again.
void Person::ShowRecords()
{
std::ifstream data("Input.txt");
while (true)
{
if (!data.good())
{
break;
}
Person person;
data >> person;
std::cout << "First Name: " << person.getFirstName() << "\n";
std::cout << "Last Name: " << person.getLastName() << "\n";
std::cout << "Phone number: " << person.getNumber() << "\n";
std::cout << "EGN: " << person.getEGN() << "\n\n";
}
}
I really think that the most didactic way of tackling stream input is:
while (true) {
// Read
// Check (if fail then break)
// Use
}
Notice the pattern: infinite loop with Read/Check/Use. Check is where we can exit the loop. First you read, than you check if the reading operation was successful or failed, than you can use the data or exit based on that.
Adapting this to your case:
void Person::ShowRecords()
{
std::ifstream data("Input.txt");
while (true) {
// Read
Person person;
data >> person;
// Check
if (!data) {
break;
}
// Use
std::cout << "First Name: " << person.getFirstName() << "\n";
std::cout << "Last Name: " << person.getLastName() << "\n";
std::cout << "Phone number: " << person.getNumber() << "\n";
std::cout << "EGN: " << person.getEGN() << "\n\n";
}
}
The non didactic and probably more idiomatic way is:
void Person::ShowRecords()
{
std::ifstream data("Input.txt");
Person person;
while (data >> person) { // Read and, immediately after, Check
std::cout << "First Name: " << person.getFirstName() << "\n"; // Use
std::cout << "Last Name: " << person.getLastName() << "\n";
std::cout << "Phone number: " << person.getNumber() << "\n";
std::cout << "EGN: " << person.getEGN() << "\n\n";
}
}
So here I have a class definition of a Car and then I create a carObject with it. I want the user to input values for all the variables in the carObject. As you see here, I have managed to get user input, but my approach to this problem is inefficient in my opinion.
I notice that all of the user inputs, except for the first one are very similar. I would like to use a loop of some kind to iterate over the declaration statements, or blocks of statements, and change the variable every time. I would like to put an if statement to enter different input only for the first iteration of the loop. I know that in bash I could use a string variable to stand for the variable name, but I don't know if that's possible in C++.
Notice that the object name does not change, but only the variables that are associated with it. I also use the same word for the user input, which preferably should be changed every iteration. I also have a series of arrays which are named similarly. The purpose of these arrays is to tell the user what options are available for a particular variable.
Although I have previous programming experience, I am relatively new to C++. A block of code that would serve as a solution to my problem that involves a call to another function would suit my purposes. Here is my code below.
#include <iostream>
#include <string>
using namespace std;
class Car {
public:
string Name;
string Model;
string Color;
string Transmission;
string Category;
};
int main() {
Car CarObject;
string modelOptions [3] = { "Ferrari", "Porsche", "Nissan" };
string colorOptions [4] = { "Blue", "Red", "Green", "White" };
string transmisionOptions [2] = { "Automatic", "Manual" };
string categoryOptions [3] = { "A", "B", "C" };
cout << "Enter " << "name" << " for Car 1." << endl;
cin >> carObject.Name;
cout << endl;
cout << "Enter " << "model" << " for Car 1." << endl;
cout << "Options are:";
for (const string &text: modelOptions) {
cout << " " << text;
}
cout << "." << endl;
cin >> carObject.Model;
cout << endl;
cout << "Enter " << "color" << " for Car 1." << endl;
cout << "Options are:";
for (const string &text: colorOptions) {
cout << " " << text;
}
cout << "." << endl;
cin >> carObject.Color;
cout << endl;
cout << "Enter " << "transmission" << " for Car 1." << endl;
cout << "Options are:";
for (const string &text: transmissionOptions) {
cout << " " << text;
}
cout << "." << endl;
cin >> carObject.Transmission;
cout << endl;
cout << "Enter " << "category" << " for Car 1." << endl;
cout << "Options are:";
for (const string &text: categoryOptions) {
cout << " " << text;
}
cout << "." << endl;
cin >> carObject.Category;
cout << endl;
...
return 0;
}
void Car::InputParameter(string& param, const string &msg, const vector<string>& options)
{
cout << msg << endl;
for (const string &text: options) {
cout << " " << text;
}
cout << "." << endl;
cin >> param;
cout << endl;
}
I think you might want something like this. You just call it for each member.
This block of code:
cout << "Enter " << "category" << " for Car 1." << endl;
cout << "Options are:";
for (const string &text: categoryOptions) {
cout << " " << text;
}
cout << "." << endl;
cin >> carObject.Category;
cout << endl;
… can be replaced with a call to a function like this:
carObject.Category = userInput( "category", categoryOptions );
Clearly it returns a string (that is, a std::string).
The options argument should better be made a vector<string>.
Then just replace the other similar blocks with ditto calls to that function.
Is it a good idea to make that function a member function of Car?
No.
Consider, for example, how to then use Car in a GUI program (Graphical User Interface).
I'm developing a simple console based email application.
In my app, messages are stored in std::vector, and I want to add possibility to delete messages.
How can I delete a element from a vector?
Here's my code:
//MAIN MENU OPTION 1 SELECTED:
// print list of all messages to the console
void viewInbox() {
vector<Message> inbox{
Message("jayleno#hotmail.com", "Knife you tonight", "Hey Sam, what kind of suit do you wanna be buried in?!"),
Message("paypalservices#hotmail.com", "Urgent from paypal.com", "Dear paypal user, someone has hacked your account. Give us your password now so we change it!"),
};
cout << "You have " << inbox.size() << " new messages.\n";
cout << "Index Subject" << '\n';
for (int i = 0; i < inbox.size(); ++i)
std::cout << "Message " << i << ": " << inbox[i].getSubject() << '\n';
cout << "Please enter number of message you would like to view\n";
int read;
cin >> read;
cout << "From: " << inbox[read].getAddress() << '\n';
cout << "Subject: " << inbox[read].getSubject() << '\n';
cout << inbox[read].getMessageText() << '\n';
cout << "To erase this message press 1\n";
//Code here for deleting a message...
}//end of viewInbox()
To erase a message from the vector use vector::erase which takes an iterator.
The easiest way to get an iterator to a particular message is to use inbox.begin() + message_number.
I have refactored your code into smaller functions to make the code easier to work with:
void viewMessage(vector<Message>& messages, size_t message_number) {
vector<Message>::iterator message = messages.begin() + message_number;
cout << "From: " << message->getAddress() << endl;
cout << "Subject: " << message->getSubject() << endl;
cout << message->getMessageText() << endl;
cout << "To erase this message press 1\n";
int erase;
cin >> erase;
// Maybe do some error checking on cin...
if (erase == 1) {
messages.erase(message);
}
}
void viewMessages(vector<Message>& messages){
auto inbox_size = messages.size();
cout << "You have " << inbox_size << " new messages.\n";
cout << "Index Subject" << '\n';
for (size_t i = 0u; i != inbox_size; ++i) {
std::cout << "Message " << i << ": " << messages[i].getSubject() << '\n';
}
cout << "Please enter number of message you would like to view\n";
size_t message_number;
cin >> message_number;
// Maybe do some error checking on cin...
// And some bounds checking of message_number...
viewMessage(messages, message_number);
}
void viewInbox() {
vector<Message> inbox{
Message{ "jayleno#hotmail.com", "Knife you tonight", "Hey Sam, what kind of suit do you wanna be buried in?!" },
Message{"paypalservices#hotmail.com", "Urgent from paypal.com", "Dear paypal user, someone has hacked your account. Give us your password now so we change it!"},
};
viewMessages(inbox);
// Maybe call viewMessages again to see results of erase...
}
As I have commented, watch out for error checking on your input.
I need help getting declared string function to change white space of input file to a specific character.
if (infile.fail())
{
cout << "The file doesn't exist";
exit(-1);
}
else
{
numBooks = readFile (infile, magSub, 260);
for (i=0; i<numBooks; i++)
{
cout << "Last Name: " << magSub[i].lastName << endl;
cout << "First Name: " << magSub[i].firstName << endl;
cout << "Street Address: " << magSub[i].address << endl;
cout << "City: " << magSub[i].city << endl;
cout << "State or Province: " << magSub[i].state << endl;
cout << "Country: " << magSub[i].country << endl << endl;
cout << "Zip or Postal Code: " << magSub[i].zip << endl;
cout << "Expiration Date: " << magSub[i].expDate << endl;
cout << "Subscriber Number: " << magSub[i].subNum << endl << endl;
}
writeFile(outfile, magSub, numBooks);
}
}
void fillSpace (string &expDate)
{
for (int i=0; expDate.length(); i++)
{
if (isspace(expDate[i]))
expDate[i] = '0';
}
}
I have the function declared above main. I know I need to call the function but I can't get it to change the white spaces.
In your code for fillSpace, you are not checking for the end of string condition. You should use i<expDate.length() for checking the end of string.
You have missed the check condition in for loop of fillSpace function.
for (int i=0; i < expDate.length(); i++)
And for calling the function
you have to declare a string which will store the string from the magSub[i].expDate.
and then pass that string to the function fillSpace.
After that you will get the string with replaced char space with '0'.
cout << "Expiration Date: " << magSub[i].expDate << endl;
please use the following code:
string temp = magSub[i].expDate; // copy the string to the temp string/char array
fillSpace (temp); // Missing Line for function call
cout << "Expiration Date: " << temp << endl; // replace line with
Hope
this will Help you.