-1

I'm learning C++. I'm developing a simple "library management" application that allows users to create an account, check out books, etc. Each book is managed using a unique text file. The text file contains three lines as follows, however the third line is the only important thing here, as it contains the owner of the book.

Screenshot Of Text File

The following code prints the contents of an additional text file that contains a list of all the books, but that shouldn't be relevant to the error. It converts the contents of the text file to a string, and then checks to see if "NA" is present. If "NA" is present, it is replaced with the current username. The file is then reopened using ios::trunc to wipe the file, and the new string is passed into the file. This works fine.

The issue is that when running the application, if a username is already there instead of "NA", I get a Debug Error that only reads abort() has been called. I've tried debugging, but I can't get any more information.

This is the error and the code:

Error

void bookCheckout()
{
    system("CLS");
    string line;
    string bookChoice;
    ifstream checkBookList;
    ofstream checkOutBook;
    checkBookList.open("books/booklist.txt");
    
    string sTotal;
    string s;

    

    cout << "<---Avaliable Books--->" << endl;
    while (getline(checkBookList, line)) {
        cout << line << endl;
    }
    checkBookList.close();

    cout << "\nWhat Book Would You Like?:";
    cin >> bookChoice;

    checkBookList.open("books/" + bookChoice + ".txt");
    while (!checkBookList.eof()) {
        getline(checkBookList, s);
        sTotal += s + "\n";
    }

    checkBookList.close();
    if (sTotal.find("NA")) {
        sTotal.replace(sTotal.find("NA"), 2,  globalUsername);
        checkOutBook.open("books/" + bookChoice + ".txt", ios::trunc);
        checkOutBook << sTotal;
        checkOutBook.close();
    }
    else if (!sTotal.find("NA")) {
        
        cout << "Book already checked out!" << endl;
    }
    
    checkOutBook.close();
    


    
    system("PAUSE");

    

}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Your error dialog has `Retry` button; did you click it? – Vlad Feinstein Sep 01 '21 at 23:57
  • I pressed the Retry button and I got a box saying "Library Management System.exe has stopped working" – Ryan Smith Sep 01 '21 at 23:59
  • If you build the `Debug` configuration and run "Debug" (`F5`), that retry should take you to the place of failure. – Vlad Feinstein Sep 02 '21 at 00:06
  • 1
    `while (!checkBookList.eof()) {` - see [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/) `if (sTotal.find("NA")) {` - `string::find()` returns an index, not a boolean. And if your goal is to find a LINE that says `"NA"`, using `find()` is not the best choice for that purpose. `else if (!sTotal.find("NA")) {` - no need to call `find()` in the `else` at all – Remy Lebeau Sep 02 '21 at 00:07
  • I added the proper code and it works. Thank you so much. I'm new to C++ as I'm sure you can tell. – Ryan Smith Sep 02 '21 at 00:15

1 Answers1

0

There are a few issues with your code:

Also, your goal is to check if the 3RD LINE SPECIFICALLY is "NA", so using string::find() is not the best choice for that purpose. Think of what would happen if the 1st or 2nd line happened to contain the letters NA. Your code logic would not behave properly.

  • else if (!sTotal.find("NA")) - no need to call find() in the else at all. Just use else by itself.

With that said, try something more like this:

void bookCheckout()
{
    system("CLS");

    ifstream checkBookList;

    checkBookList.open("books/booklist.txt");
    if (!checkBookList.is_open()) {
        cerr << "Unable to open booklist.txt" << endl;
        return;
    }

    string line;

    cout << "<---Available Books--->" << endl;
    while (getline(checkBookList, line)) {
        cout << line << endl;
    }
    checkBookList.close();

    cout << "\nWhat Book Would You Like?:";

    string bookChoice;
    getline(cin, bookChoice);

    checkBookList.open("books/" + bookChoice + ".txt");
    if (!checkBookList.is_open()) {
        cerr << "Unable to open " + bookChoice + ".txt for reading" << endl;
        return;
    }

    string sTotal, sOwner;
    int lineNum = 0;
    string::size_type ownerIndex = string::npos;

    while (getline(checkBookList, line)) {
        ++lineNum;
        if (lineNum == 3) {
            sOwner = line;
            ownerIndex = sTotal.size();
        }
        sTotal += line + "\n";
    }

    checkBookList.close();

    if (sOwner == "NA") {
        sTotal.replace(ownerIndex, 2, globalUsername);

        ofstream checkOutBook("books/" + bookChoice + ".txt", ios::trunc);
        if (!checkOutBook.is_open()) {
            cerr << "Unable to open " + bookChoice + ".txt for writing" << endl;
            return;
        }

        checkOutBook << sTotal;
        checkOutBook.close();

        cout << "Book checked out!" << endl;
    }
    else {
        cout << "Book already checked out by " << sOwner << "!" << endl;
    }

    system("PAUSE");
}

Alternatively, use a std::vector to gather the book contents, that will give you indexed access to each line:

#include <vector>

void bookCheckout()
{
    system("CLS");

    ifstream checkBookList;

    checkBookList.open("books/booklist.txt");
    if (!checkBookList.is_open()) {
        cerr << "Unable to open booklist.txt" << endl;
        return;
    }

    string line;

    cout << "<---Available Books--->" << endl;
    while (getline(checkBookList, line)) {
        cout << line << endl;
    }
    checkBookList.close();

    cout << "\nWhat Book Would You Like?:";

    string bookChoice;
    getline(cin, bookChoice);

    checkBookList.open("books/" + bookChoice + ".txt");
    if (!checkBookList.is_open()) {
        cerr << "Unable to open " + bookChoice + ".txt for reading" << endl;
        return;
    }

    vector<string> sTotal;
    sTotal.reserve(3);

    while (getline(checkBookList, line)) {
        sTotal.push_back(line);
    }

    while (sTotal.size() < 3) {
        sTotal.push_back("");
    }

    checkBookList.close();

    if (sTotal[2] == "" || sTotal[2] == "NA") {
        sTotal[2] = globalUsername;

        ofstream checkOutBook("books/" + bookChoice + ".txt", ios::trunc);
        if (!checkOutBook.is_open()) {
            cerr << "Unable to open " + bookChoice + ".txt for writing" << endl;
            return;
        }

        for(size_t i = 0; i < sTotal.size(); ++i) {
            checkOutBook << sTotal[i] << '\n';
        }
        checkOutBook.close();

        cout << "Book checked out!" << endl;
    }
    else {
        cout << "Book already checked out by " << sTotal[2] << "!" << endl;
    }

    system("PAUSE");
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770