В этом коде есть ряд проблем и вредных привычек.
using namespace std;
Это опасная практика, поскольку это означает, что все имена, объявленные в пространстве имен std
библиотеки импортируются в глобальное пространство имен, которое заставит вас все виды странных проблем в будущем.
Если вы хотите, чтобы избежать необходимости вводить std::cout
и std::string
все время, вы можете вместо этого написать:
using std::string;
using std::cout;
Далее:
typedef struct book { ... } Book
Это язык C конструкция, которая совершенно не нужна в C++.
struct Book {
};
это так просто.
struct Book {
char title[100];
};
Поскольку вы знаете о std :: string, почему бы не использовать его здесь?
using std::string;
struct Book {
string title;
string author;
string publisher;
float price;
int isbn;
int pages;
int copies;
};
indexBook
Функция откровенно страшно.
void
indexBook(Book *my_book)
{
memcpy(&collection[currentIndex], my_book, sizeof(Book));
currentIndex++;
}
memcpy
следует избегать в C++, вместо него вы должны полагаться на операторов копирования/назначения/перемещения встроенных объектов на C++.
collection[currentIndex] = *my_book;
Если есть особенно хорошая причина, почему объект не должен быть скопирован, и автор класса объекта ничего хорошего, вы получите ошибку компиляции, в то время как с memcpy
вы просто получите неопределенное поведение.
Позволяет пропустить немного вперед:
void
printCollection(void)
{
...
}
void printCollection(void);
нет никакого вреда здесь, но вперед объявляя printCollection после его определения является излишним.
Book *my_book = new Book;
indexBook(my_book);
...
delete my_book;
Непонятно, почему вы это сделали. Это кажется расточительным. Вы можете так же легко сделать:
Book my_book;
Но в любом случае есть простая задача: my_book
не был инициализирован. То, что вы скопировали в сборник, - это догадка.
Book my_book {};
будет по умолчанию инициализировать его для вас.
Теперь давайте посмотрим на файл readfile.
fstream my_stream;
string line = " "; // why?
my_stream.open("input.txt");
int i=0;
for (i = 0; ...)
{
...
}
my_stream.close();
Здесь вы делаете много ненужной работы и кодирования.Вы используете C++, чтобы объекты имели конструкторы, вы можете написать std::string line = " "
или std::string line(" ")
или std::string line {" "}
(предпочтительнее, чем C++ 11). Но вы также можете сделать то же самое для fstream. И, наконец, если вам не нужно, чтобы i
был видимым вне цикла for, вы можете сделать его локальным в цикле.
Потому что fstream
- это объект, он также имеет деструктор, который гарантирует, что файл будет закрыт.
Все это приводит нас к
void
readfile()
{
std::fstream my_stream("input.txt");
for (int i = 0; i < currentIndex; ++i)
{
std::string line {}; // default initialized.
...
}
// call to close is redundant
}
Теперь мы подходим к сути того, что на самом деле сломана с вашим кодом, большинство из которых находится в этой функции.
1 Вы не убедитесь, что файл открыт
2 Вы называете getline(my_stream, line)
в цикле в то время как внутри в i
для петли,
3 Вы не приспосабливаются currentIndex
4 (хуже всего) вы злоупотребляете operator>>
.
В своем коде вы пишете следующее:
for (i = 0; i < currentIndex; ++i) // *1
{
while (getline(my_stream, line)) // *2
{
cin >> line >> collection[i].title; // *3
Потому что вы назвали indexBook
на неинициализированном объекте в основном currentIndex
является 1, поэтому мы вводим для цикла.
Теперь мы попробуем прочитать первую строку my_stream
в line
(* 2), и если это удастся, мы вводим тело цикла while.
Сейчас мы пытаемся прочитать слово из std::cin
в строку следует другим словом из std::cin
в коллекцию [0] .title (* 3, помните: I = 0, и должны быть < currentIndex
).
Вы не проверяете эти std::cin
, если кто-либо из них не работает, он просто продолжит все, ничего не делая.
Наконец, мы доходим до конца цикла while, и мы снова пробуем getline(my_stream, line)
. Если это удастся, мы снова войдем в тело цикла while.
* Обратите внимание, что i
еще не был изменен, так что код по-прежнему относится к collection[0]
, переписав все данные, которые извлекаются из std::cin
в первом проходе.
Это будет продолжаться до тех пор, пока мы не исчерпаем my_stream
, после чего мы, наконец, выйдем из цикла while и достигнем цикла for
.
i
увеличивается, так что i
теперь равен 1
. Условие проверено и больше не верно, что i < currentIndex
, поэтому мы выходим из цикла for.
Кажется, вы считали, что пишете код, чтобы прочитать слово от линии до collection[i].title
.Мы можем использовать std::stof
и std::stoi
для преобразования строк в float
и integer
таким образом, мы могли бы написать что-то вроде этого:
bool
readFile(void) // returns true on success, false on error
{
using std::getline;
std::fstream my_stream("input.txt");
if (!my_stream.is_open())
return false;
for (int i = 0; i < currentIndex; ++i)
{
Book newBook {}; // temporary local to read into
if (!getline(my_stream, newBook.title))
break;
if (!getline(my_stream, newBook.author))
break;
if (!getline(my_stream, newBook.publisher))
break;
std::string line;
if (!getline(my_stream, line))
break;
newBook.price = std::stof(line);
if (!getline(my_stream, line))
break;
newBook.isbn = std::stoi(line);
if (!getline(my_stream, line))
break;
newBook.pages = std::stoi(line);
if (!getline(my_stream, line))
break;
newBook.copies = std::stoi(line);
collection[i] = newBook;
}
return true;
}
Это, безусловно, далеко от того, идиоматических C++, но это для учителя/книги, чтобы научить вас. Тем не менее, я перейду к тому, как я разрешу это задание.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using std::string;
struct Book
{
// suffix member variables with `_` to distinguish from function names
// and parameters.
string title_;
string author_;
string publisher_;
float price_;
int isbn_;
int pages_;
int copies_;
// support `stream >> book` syntax with `operator >>()`.
// note that it's not a member of the class, so we declare
// it as a `friend` function so it can have full access.
friend std::istream& operator >> (std::istream&, Book&);
// because of the formatting, I wouldn't make this `operator <<`.
void print() const
{
std::cout << "Title: " << "\t\t" << title_ << '\n';
std::cout << "Author: " << "\t" << author_ << '\n';
std::cout << "Publisher: " << "\t" << publisher_ << '\n';
std::cout << "Price: " << "\t\t" << price_ << '\n';
std::cout << "ISBN: " << "\t\t" << isbn_ << '\n';
std::cout << "Pages: " << "\t\t" << pages_ << '\n';
std::cout << "Copies: " << "\t" << copies_ << '\n';
}
};
using Collection = std::vector<Book>;
std::istream& operator >> (std::istream& str, Book& book)
{
std::getline(str, book.title_);
std::getline(str, book.author_);
std::getline(str, book.publisher_);
str >> book.price_ >> book.isbn_ >> book.pages_ >> book.copies_;
str.ignore();
return str;
}
bool
readFile(std::string filename, Collection& collection)
{
std::fstream my_stream(filename);
if (!my_stream.is_open())
return false;
Book newBook {};
while (my_stream >> newBook)
collection.emplace_back(std::move(newBook));
return true;
}
void
printCollection(const Collection& collection)
{
for (auto&& book : collection) // or: const Book& book
{
book.print();
std::cout << '\n';
}
}
int
main()
{
Collection collection;
if (!readFile("input.txt", collection))
{
std::cerr << "readFile on input.txt failed\n";
return 1; // non-zero return from main indicates program failure
}
std::cout << "Read " << collection.size() << " books\n\n";
printCollection(collection);
}
Если вы используете C++, вам следует действительно использовать 'std :: string' вместо массивов char. –
Поиск в Интернете для «stackoverflow C++ read file struct» для примеров того, как читать файл данных в структуру. Слишком много подобных вопросов и ответов уже. –
Подсказка: похоже, что вы только увеличиваете 'currentIndex' когда-либо, независимо от того, сколько книг находится в вашем txt-файле. –