I have been doing c++ for somewhere between 1 1/2 and 3 months (I'm awful keeping up with time). I have a program (one of my first) that I have taken and added pieces to since I made it and it has come to be the piece of work it is now. It is an address book program that uses a pointer to a structure I created within a class that contains necessary functions. It may be completely inefficient, but as I said, this is my first program (although I have been working on it for a month and a half if not longer).
Now to the problem. I have it set up to save the book to a text file that you can name (default "address_book.txt"). If I save or import from the default file, everything works perfectly and my program runs without problem. If I attempt to access any file apart from the default (like "mybook.txt"), however, I receive an error message "Segmentation fault (core dumped)"
I don't know if I should include the entire code as it has grown to be quite long. If I wasn't supposed to, I sincerely apologize, but it will likely be far more helpful to anyone trying to assist me.
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
template <typename out_type, typename in_value> //used to cast any variable to a string
out_type stream_cast(const in_value & t)
{
stringstream ss;
ss << t;
out_type result;
ss >> result;
return result;
}
struct addBook
{
string name;
string address;
string number;
};
void lowercase (string& str) //make all letters of a string lower case : "MY STRING" -> "my string"
{
for (int a=0; a<str.length(); a++)
{
if (str[a] >= 0x41 && str[a] <= 0x5A)
str[a]+=0x20;
}
}
void capitalize (string& str) //capitalize first letter of a string : "my string" -> "My string"
{
if (str[0] >= 0x61 && str[0] <= 0x7A)
str[0]-=0x20;
for (int a=1; a<str.length(); a++)
{
if (str[a] >= 0x41 && str[a] <= 0x5A)
str[a]+=0x20;
}
}
void title (string& str) //capitalize first letter of each word : "my string" -> "My String"
{
if (str[0] >= 0x61 && str[0] <= 0x7A)
str[0]-=0x20;
for (int a=1; a<str.length(); a++)
{
if (str[a] >= 0x41 && str[a] <= 0x5A)
str[a]+=0x20;
if (str[a-1] == 0x20 && str[a] >= 0x61 && str[a] <= 0x7A)
str[a]-=0x20;
}
}
void uppercase (string& str) //capitalize entire string : "my string" -> "MY STRING"
{
for (int a=0; a<str.length(); a++)
{
if (str[a] >= 0x61 && str[a] <= 0x7A)
str[a]-=0x20;
}
}
class address_book
{
addBook * book1, * book2; //book1 is current book, book2 is used to replace lost entries
int var1, var2, choice; //var1 is length of book1, var2 is length of book2, choice is used for input
string retstr, name, address, number; //retstr is used in string functions
public:
address_book ();
address_book (int);
address_book operator + (address_book); //used to append two address books
void newEntry(string, string, string); //used to add an entry to current book
void showEntry(string); //used to display entries
void delEntry(string); //used to delete entries
void saveBook(char*); //used to save the current book to file named (char*)
void saveBook(const char*); //alternative for previous function
void importBook(char*); //used to import and append book (char*) to current book
void importBook(const char*); //alternative for previous function
string shortEntry(); //returns a list containing entry number and name( [#] : name )
string readEntry(string); //returns a formatted list containing specified entries in curren book
};
int main()
{
ifstream test; //used to check if files exist
string choice,input,name,add,num;
char * fname, y_n;
address_book mybook;
cout << "Welcome to address_book Editor v 0.1\n" << endl;
while (choice != "quit" && choice != "7")
{
cout << "1) Create Entry\n2) Read Entry\n3) Delete Entry\
\n4) Save Book\n5) Import Book\n6) Clear\n7) Quit\nInput Choice -> ";
getline (cin, choice);
lowercase(choice);
if (choice == "1" || choice == "create" || choice == "create entry" || choice == "new")
{
cout << "\nName ->"; //get and store the name
getline (cin, name);
cout << "\nAddress ->"; //get and store the address
getline (cin, add);
cout << "\nNumber ->"; //get and store the number
getline (cin, num);
title(name); //capitalize first letter of each word
title(add); //in given values
title(num);
mybook.newEntry(name, add, num);
}
else if (choice == "2" || choice == "read" || choice == "read entry")
{
if (mybook.shortEntry() == "")
cout << "\nNo entries to read\n" << endl;
else
{
cout << "\nRead Which Entry Number? (all reads all entries)\n" + mybook.shortEntry() + "->";
getline (cin, input); //accepts entry number
mybook.showEntry(input); //and sends to showEntry()
}
}
else if (choice == "3" || choice == "delete" || choice == "delete entry" || choice == "del")
{
if (mybook.shortEntry() == "")
cout << "\nNo entries to delete\n" << endl;
else
{
cout << "\nDelete Which Entry Number? (all deletes all entries)\n" + mybook.shortEntry() + "->";
getline (cin, input); //accepts entry number
mybook.delEntry(input); // and sends it to delEntry()
}
}
else if (choice == "4" || choice == "save" || choice == "save book")
{
try
{
cout << "Enter name of file to save to\n[address_book.txt]->";
getline(cin, input);
if (input == "") //default file is "address_book.txt"
fname = "address_book.txt";
else
fname = const_cast<char *> (stream_cast<char *> (input));
test.open(const_cast<char *>(fname), ios::ate); //open fname and check
if (test.tellg() > 0) //if it exists
{
test.close();
cout << endl << "File already exists\nOverwrite? [y,n] ->";
getline(cin, input);
lowercase(input);
if (input == "")
y_n = 'y';
else
y_n = input[0];
if (y_n == 'y')
mybook.saveBook(fname);
else
cout << "Save cancelled" << endl;
}
else
mybook.saveBook(fname);
if (test.is_open())
test.close();
cout << endl;
}
catch (exception& e)
{
cout << "Error while saving occured " << e.what() << endl;
}
}
else if (choice == "5" || choice == "import" || choice == "import book")
{
try
{
test.close();
cout << "Enter name of file to import\n[address_book.txt]->";
getline(cin, input);
if (input == "") //default file is "address_book.txt"
fname = "address_book.txt";
else
fname = const_cast<char *> (stream_cast<char *> (input));
test.open(const_cast<char *>(fname), ios::ate); //open fname and
if ((int) test.tellg() == 0) //check if it exists
cout << "Error, file is empty or does not exist." << endl;
else
mybook.importBook(fname);
if (test.is_open())
test.close();
cout << endl;
}
catch (exception& e)
{
cout << "Error in importing occured " << e.what() << endl;
}
}
else if (choice == "6" || choice == "clear") //used to clear a crowded screen
system("clear");
else if (choice != "7" && choice != "quit")
cout << "Invalid choice\n" << endl;
}
if (mybook.readEntry("all") != "Invalid Entry\n\n") //while closing, if number of entries > 0
mybook.showEntry("all"); //displays the current book
return 0;
}
address_book::address_book ()
{
var1=var2=0;
}
/* currently useless instantiation, creates unreadable entries
address_book::address_book (int a)
{
var1=var2=a;
}*/
address_book address_book::operator+ (address_book part)
{
address_book temp;
temp.var1 = var1 + part.var1;
temp.book1 = new addBook[temp.var1];
for (int a = 0, b = 0; a < temp.var1; a++)
{
if (a < var1) // stores to temp until a is = to number of entries in
{ // book1 then goes to part.book1 and starts over with b
*(temp.book1+a) = *(book1+a);
}
else
{
*(temp.book1+a) = *(part.book1+b);
b++;
}
}
temp.book2 = temp.book1;
temp.var2 = temp.var1;
return temp;
}
void address_book::newEntry(string name, string address, string number)
{
book1 = new addBook[++var1]; //adds 1 to number of entries in book1
(book1+var1-1)->name = name;
(book1+var1-1)->address = address;
(book1+var1-1)->number = number;
cout << "\n" << endl;
if (var1>1) //if number of entries in book1 > 1
for (int a = 1; a < var1; a++) //uses entries in book2 to replace
*(book1+a-1) = *(book2+a-1);//entries lost from book1
book2 = book1;
var2 = var1;
}
void address_book::showEntry(string input) //accepts entry choice, and uses readEntry to display it
{
cout << endl << readEntry(input);
}
void address_book::delEntry(string input)
{
if (input == "all")
{
book1 = book2 = new addBook[0];
var1 = var2 = 0;
}
else
{
stringstream(input) >> choice;
if (choice <= var1 && choice > 0)
{
book1 = new addBook[--var1]; //reduces length of book1 by 1
for (int a = var2 = 0; a <= var1; a++)
{ //goes through book2 and replaces entries lost in book1
if (a == (choice-1)) continue;//skips deleted entry
*(book1+var2) = *(book2+a);
var2++;
}
book2 = book1; //book2 becomes new book1
var2 = var1;
cout << "\n" << endl;
}
else
cout << "\nInvalid Choice\n" << endl;
}
}
string address_book::readEntry(string inp)
{
capitalize(inp);
if (inp == "All" && var1 > 0)
{
retstr = "Address Book\n\n"; // loops each entry and returns
for (choice = 0; choice < var1; choice++)// the entry in format
{
name = (book1+choice)->name; // stores entries information
address = (book1+choice)->address;// to corresponding display
number = (book1+choice)->number; // variable
retstr += "Name:\t\t" + name + "\nAddress:\t"
+ address + "\nNumber:\t\t" + number + "\n\n";
}
}
else
{
stringstream(inp) >> choice; // if choice != all, selects
if (choice <= var1 && choice >0) // entry number in choice and
{ // prints its values
choice--;
name = (book1+choice)->name; // stores entries information
address = (book1+choice)->address;// to corresponding display
number = (book1+choice)->number; // variable
retstr = "Name:\t\t" + name + "\nAddress:\t"
+ address + "\nNumber:\t\t" + number + "\n\n";
}
else
retstr = "Invalid Entry\n\n";
}
return retstr;
}
string address_book::shortEntry() //used for cenvenience while reading or deleting entries
{
if (var1 > 0) //returns a short version of each entry according to this format
{ //[entry#]: entry_name
retstr = "Current Entries\n";
for (choice = 0; choice < var1; choice++)
retstr += "[" + stream_cast<string>(choice+1) + "]:\t"+ (book1+choice)->name + "\n";
}
else
retstr = "";
return retstr;
}
void address_book::saveBook(char * fname)
{
try
{
ofstream bookfile;
bookfile.open(const_cast<char *> (fname), ios::trunc); //deletes previous file and reopens it
bookfile << readEntry("all"); //reads in each entry using readEntry()
bookfile.close();
}
catch (exception& e)
{
cout << "Error while saving occured : " << e.what() << endl;
}
}
void address_book::saveBook(const char * fname)
{
try
{
ofstream bookfile;
bookfile.open(fname, ios::trunc); //deletes previous file and reopens it
bookfile << readEntry("all"); //read in each entry using readEntry()
bookfile.close();
}
catch (exception& e)
{
cout << "Error while saving occured : " << e.what() << endl;
}
}
void address_book::importBook(char * fname)
{
try
{
ifstream bookfile;
string line;
bookfile.open(const_cast<char *> (fname)); //opens file and checks if it opened correctly
if (bookfile.is_open())
while (! bookfile.eof())
{
getline(bookfile,line);
if (line.substr(0,4) == "Name")
{
name = line.substr(line.find("\t\t")+2); //reads locations of
getline(bookfile,line); //name, address, and number
address = line.substr(line.find("\t")+1);//and stores them to variables
getline(bookfile,line); //to send to newEntry()
number = line.substr(line.find("\t\t")+2);
newEntry(name,address,number);
}
}
}
catch (exception& e)
{
cout << "Error while importing occurred : " << e.what() << endl;
}
}
void address_book::importBook(const char * fname)
{
try
{
ifstream bookfile;
string line;
bookfile.open(fname);
if (bookfile.is_open())
while (! bookfile.eof())
{
getline(bookfile,line);
if (line.substr(0,4) == "Name")
{
name = line.substr(line.find("\t\t")+2); //reads locations of
getline(bookfile,line); //name, address, and number
address = line.substr(line.find("\t")+1);//and stores them to variables
getline(bookfile,line); //to send to newEntry()
number = line.substr(line.find("\t\t")+2);
newEntry(name,address,number);
}
}
}
catch (exception& e)
{
cout << "Error while importing occurred : " << e.what() << endl;
}
}
I would appreciate any tips on efficiency or help regarding the "Segmentation Fault." Thank you all in advance.