-1

I am making a library system and it should read book details from a file it is formed like this:

1.The Alchemist, Paulo Coelho, Fiction, 978-0-06-112073-3, true, false
2.The Lord of the Rings, J.R.R. Tolkien, Fantasy, 978-0-345-39195-8, true, false

And here is the code:

From book class trying to overload >>.

istream &operator>>(istream &in, book &b1)
{

    fstream file;
    file.open("/Users/fatmaomara/Desktop/DR.Mahmoud/tasks/catalogbooks.txt");

    if (!file.is_open())
    {
        cout << "Could not open file." << endl;
        return in;
    }
    cout << "Reading From File ....\n";

    getline(file, b1.title, ',');
    getline(file, b1.author, ',');
    getline(file, b1.genre, ',');
    getline(file, b1.ISBN, ',');
    file >> b1.av;
    file >> b1.br;

    // file.close();
    cout << "Successfully Read !\n";
    return in;
}

and then I call it in the library class to add the book in the list like this:

void addbook()
{
    book b1;
    int n;
    cout << "How Many Books Do You Want To Add ? ";
    cin >> n;
    // cin >> b1;
    //  catalog.insertfirst(b1);
    for (int i = 0; i < n; i++)
    {
        cin >> b1;
        catalog.insertlast(b1);
        counter++;
    }
}

It keeps reading the first line and I want it to read the required number of lines, what I am doing wrong?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
omara
  • 1
  • 1
  • 3
    Your operator opens the same file every time so it will read the first line from that file every time. You should open the file once and use the stream parameter `in` to read from. – Retired Ninja Aug 12 '23 at 20:45
  • 1
    Something like `std::ifstream f("filename"); book b; while (f >> book) { /* do something with book */ }` – Retired Ninja Aug 12 '23 at 20:47
  • 1
    When you overload `operator>>` the first parameter (you called it `in`) is where you read from. You're not suppose to open a completely different input inside of `operator>>`. – john Aug 12 '23 at 21:10

2 Answers2

0

You should rearrange your code so that file opening happens outside of operator>>. The opened file is then passed to operator>>. Something like this

istream &operator>>(istream &in, book &b1)
{
    cout << "Reading ....\n";

    getline(in, b1.title, ',');
    getline(in, b1.author, ',');
    getline(in, b1.genre, ',');
    getline(in, b1.ISBN, ',');
    in >> b1.av;
    in >> b1.br;

    cout << "Successfully Read !\n";
    return in;
}

void addbook()
{
    fstream file;
    file.open(
    "/Users/fatmaomara/Desktop/DR.Mahmoud/tasks/catalogbooks.txt");

    if (!file.is_open())
    {
        cout << "Could not open file." << endl;
        return;
    }
    book b1;
    int n;
    cout << "How Many Books Do You Want To Add ? ";
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        file >> b1;
        catalog.insertlast(b1);
        counter++;
    }
john
  • 85,011
  • 4
  • 57
  • 81
  • I tried it , also same problem . the two functions are in different files – omara Aug 12 '23 at 21:24
  • @FTOMARA I can only think that you did not implement the above code correctly. Different files makes no difference. – john Aug 13 '23 at 07:00
  • @FTOMARA As explained above (and in the comments) the basic problem is that your code repeatedly reopens the same file, so it repeatedly reads the same first line. That's the problem you need to solve one way or another. – john Aug 13 '23 at 07:01
0

There are a few problems with the code you've shown.

  1. operator>> opens the file every time and ignores the stream that is passed in. This will always read the first line from the file.
  2. Mixing std::getline and >> when reading from the file is messy. See Why does std::getline() skip input after a formatted extraction? for more details.
  3. The lines using >> won't work for two reasons. You need to use std::boolalpha to read the words true and false, and there's a semicolon between them which is definitely not a bool and would cause an error.

Here's an example I put together on ideone

It writes a test file using the data you supplied and an extra line with made up data to verify that "false; true" reads correctly since both of your lines had "true; false".

It opens the file and reads the data into a book structure that should resemble yours using operator>> and adds it to a vector.

It writes the data from the vector to std::cout using operator<< in the same semicolon delimited format.

I added some code from another answer to trim the strings that are read since there was a space after the semicolon in the data shown and I'm pretty sure you do not want that kept when the data is read in.

I used std::getline exclusively to avoid issues and assigned the boolean values using a simple string comparison, if the string matches "true" the bool is true and otherwise it is false. You may want something more robust like forcing the string to lowercase first. It'll depend on how confident you are about your input data.

The last getline does not include the delimiter so it will read properly since the lines do not end with a semicolon. This will work on well formatted input data. You might consider a more robust solution of reading the whole line then splitting it, making sure it has the right number of tokens, and then assigning to the book. There are plenty of examples on here of how to split strings if you need them.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

//---
// Borrowed trim from https://stackoverflow.com/a/217605/920069
#include <algorithm> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    rtrim(s);
    ltrim(s);
}
//---

struct book
{
    std::string title;
    std::string author;
    std::string genre;
    std::string ISBN;
    bool br;
    bool av;

    friend std::istream& operator>>(std::istream& in, book& b)
    {
        if (getline(in, b.title, ','))
        {
            trim(b.title);
        }
        if (getline(in, b.author, ','))
        {
            trim(b.author);
        }
        if (getline(in, b.genre, ','))
        {
            trim(b.genre);
        }
        if (getline(in, b.ISBN, ','))
        {
            trim(b.ISBN);
        }

        std::string temp;
        if (getline(in, temp, ','))
        {
            trim(temp);
            b.av = (temp == "true");
        }
        if (getline(in, temp))
        {
            trim(temp);
            b.br = (temp == "true");
        }

        return in;
    }

    friend std::ostream& operator<<(std::ostream& out, const book& b)
    {
        out << b.title << "; ";
        out << b.author << "; ";
        out << b.genre << "; ";
        out  << b.ISBN << "; ";

        // If strem is still good save and restore the flags
        // because boolalpha is persistent
        if (out)
        {
            std::ios_base::fmtflags flags = out.flags();
            out << std::boolalpha;
            out << b.av << "; ";
            out << b.br << '\n';
            out.flags(flags);
        }

        return out;
    }
};

void createTestFile()
{
    std::ofstream f("test.txt");
    if (!f)
    {
        std::cerr << "Error creating file!\n";
        exit(-1);
    }

    f << "1.The Alchemist, Paulo Coelho, Fiction, 978-0-06-112073-3, true, false\n";
    f << "2.The Lord of the Rings, J.R.R. Tolkien, Fantasy, 978-0-345-39195-8, true, false\n";
    f << "3.Made Up Book, Some Guy, Pulp, 999-0-888-7777-8, false, true\n";
}

int main()
{
    createTestFile();

    std::ifstream f("test.txt");
    if (!f)
    {
        std::cerr << "Error opening file!\n";
        exit(-1);
    }

    book b;
    std::vector<book> v;
    while (f >> b)
    {
        v.push_back(b);
    }
    for (const book& b : v)
    {
        std::cout << b;
    }
}

Hopefully this will help you put together a solution that works for you. If you have questions please ask.

Retired Ninja
  • 4,785
  • 3
  • 25
  • 35