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.

Recommended Answers

All 7 Replies

The problem occurs at line 13. "result" has no type, as no "out_type" is passed to the templated function.

Frankly, what's up with all the casting in lines 155 & 156?

What if you replaced your lines 150-176 with:

cout << "Enter name of file to save to\n[address_book.txt]->";
                getline(cin, input);
                if (input == "")       //default file is "address_book.txt"
                    input = "address_book.txt";
                test.open(input.c_str(), 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(input.c_str());
                    else
                        cout << "Save cancelled" << endl;
                }
                else
                    mybook.saveBook(input.c_str());
                if (test.is_open())
                    test.close();
                cout << endl;

You've got the user's input in string "input", just use it. Get the c-style string needed for the open function with the .c_str() method.

commented: It was very helpful +1

Thank you for your help. Using your method however, input is lost while gathering input on whether or not to delete an existing file. However, your help with the .c_str() function did inspire me to try something and it fixed it perfectly. Here is what I put in instead:

fname = const_cast<char *> (input.c_str());

I put that in for lines 155 and 193.

As for the c_str() function, what exactly does it do? Take a string and return it as a non constant character pointer? I used a small program I use to test things, and I converted string.c_str() to a char* and it didn't work until I used const_cast<char*>(string.c_str()). Anyway, thanks again vmanes.

>>As for the c_str() function, what exactly does it do?

It returns a const char * which contains the same char in the same order as the original STL string. const char* isn't the same as char *, so you can't assign one to the other. You can however cast away the constantness of the const char* and change it temporarily into char*, which is what you did.

I'm glad I got you on the right track. However, I still find something you're doing a bit unsettling, and possibly setting a habit for you that may cause problems in the future.

getline(cin, input);
                if (input == "")       //default file is "address_book.txt"
                    fname = "address_book.txt";
                else
                    fname = const_cast<char *> (input.c_str());
                test.open(const_cast<char *>(fname), ios::ate);

input is of type string. Using the c_str() method, you can simply use it directly in the file open statement. Similarly, you could assign your default name to it. There's no need I see to have the intermediate variable fname that is of type char*.

When you assign the default name to the pointer fname, you are pointing to a series of characters in the code portion of your program. You can conceivably attempt to write using this pointer, although it probably will fail at runtime.

When you assign the c-string pointer from the string object, you are not guaranteed that the pointed to data remains after some other methods of the string are called. Exactly what the pointer that c_str() points to is implementation dependent; in the case of Visual C++ it's in fact pointing to the character data of the string object. Change the c-string your pointer points to, and you've changed the string object. It may not be so in other C++ versions.

In either case, you're making the pointer fname point to memory over which you really don't have control. It's not causing any problems as you are using it now, but is this a habit you want to start? It looks to me that you would simplify things using just the string type for your input, and pass the c_str( ) returned pointer when needed. Variable fname is superfluous.

That's my 2 cents, plus inflation.

The problem with that is passing the variable to my function .saveBook(). That function runs from char pointers. I asked what kind of data .c_str() was in hopes that I might be able to use that. I understand where you are coming from, but I am not sure how to edit my .saveBook() function to accept that data type instead. What would I change from lines 345 to the end in order to accomplish this?

-------------------------------------------------------------------

Nevermind, I saw the previous post. I will attempt to fix those errors and ask again if I have any questions. Thank you both.

I hope you've seen that saveBook( char * fname) does not need to be changed at all. mybook.saveBook( input.c_str() ); should work just fine.

That is exactly what I did. And I changed lines 161 to 168, I changed y_n to a char* and accepted the input directly into it, using cin.getline(y_n, 10), changed it to lowercase, then used its value rather than compromising the file name being stored in input. I believe I have everything working up to par. Thank you all very much.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.