我应该创建一个程序来索引我的集合中的书籍。该结构包含通常的书籍信息:标题,作者,出版商等。但是,我没有输出。一个问题是标题会有空格。
/* Book Inventory assignment 2 by Heath Martens. */
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <cstring> // I had to throw this in in order to get memcpy to work.
using namespace std;
typedef struct book{
char title[100];
char author[100];
char publisher[100];
float price;
int isbn;
int pages;
int copies;
} Book;
Book collection[100];
int currentIndex;
void
indexBook(Book *my_book)
{
memcpy(&collection[currentIndex], my_book, sizeof(Book));
currentIndex++;
}
void
readfile(void)
{
fstream my_stream;
string line = " ";
my_stream.open("input.txt");
int i=0;
for (i = 0; i < currentIndex; i++)
{
while (getline(my_stream, line))
{
cin >> line >> collection[i].title;
cin >> line >> collection[i].author;
cin >> line >> collection[i].publisher;
cin >> line >> collection[i].price;
cin >> line >> collection[i].isbn;
cin >> line >> collection[i].pages;
cin >> line >> collection[i].copies;
}
}
my_stream.close();
}
void
printCollection(void)
{
int i;
for (i = 0; i < currentIndex; i++)
{
cout << "Title: " << "\t\t" << collection[i].title << endl;
cout << "Author: " << "\t" << collection[i].author << endl;
cout << "Publisher: " << "\t" << collection[i].publisher << endl;
cout << "Price: " << "\t\t" << collection[i].price << endl;
cout << "ISBN: " << "\t\t" << collection[i].isbn << endl;
cout << "Pages: " << "\t\t" << collection[i].pages << endl;
cout << "Copies: " << "\t" << collection[i].copies << endl;
}
}
void printCollection(void);
int
main(void)
{
currentIndex = 0;
Book *my_book = new Book;
indexBook(my_book);
readfile();
printCollection();
delete my_book;
return 0;
}
这是我给出的txt文件。
Magician: Apprentice
Raymond E. Feist
Spectra (January 1, 1994)
5.02
0553564943
512
1
Magician: Master
Raymond E. Feist
Spectra (January 1, 1994)
7.99
0553564935
499
1
以下是基于提供的一些示例的更新代码。
void
readfile(void)
{
fstream my_stream ("input.txt");
if(!my_stream)
{
return;
}
string line = " ";
int i=0;
for (i = 0; i < currentIndex; i++)
{
if(!std::getline(my_stream, line))
{
break;
}
memcpy(collection[currentIndex].title, line.c_str(), std::min(sizeof(collection[currentIndex].title), line.size()));
memcpy(collection[currentIndex].author, line.c_str(), std::min(sizeof(collection[currentIndex].author), line.size()));
memcpy(collection[currentIndex].publisher, line.c_str(), std::min(sizeof(collection[currentIndex].publisher), line.size()));
my_stream >> collection[currentIndex].price;
my_stream >> collection[currentIndex].isbn;
my_stream >> collection[currentIndex].pages;
my_stream >> collection[currentIndex].copies;
my_stream.ignore();
if(!my_stream)
{
break;
}
}
答案 0 :(得分:1)
主要问题是readfile函数。如注释中所述,std :: fstream可以将字符串作为第一个参数,因此不需要稍后调用open。此外,应该在对文件执行操作之前检查文件是否已打开。 std :: string不需要初始化,以便稍后在此函数中使用。这导致以下代码。
fstream my_stream("input.txt");
if(!my_stream) {
return;
}
string line;
readfile的循环似乎没有正确构造。外部循环在集合数组的当前索引范围内递增,而内部循环看起来好像是要读取整个文件。如果内部循环构造正确,那么文件的内容将被写入集合中的第0本书。内循环开始于测试是否从文件中读取一行(通过读取std :: cin的输入而忽略)成功。
因为input.txt中的“Books”数量未知,所以外部循环似乎与将所有“Books”读入另一个集合元素的目标无关。为了读取整个文件,我们将更改循环以检查文件是否仍然可读。
while(my_stream) {
为了读取字符串,我们需要使用std :: getline读取和存储整行(my_stream >> line;
这里将被空格绊倒),并将memcpy复制到相应的行内容中char数组。因为std :: getline可能会失败,所以我们在使用line的内容之前检查是否成功。例如,这里的标题是:
// title
if(!std::getline(my_stream, line)) {
break;
}
memcpy(collection[currentIndex].title, line.c_str(), std::min(sizeof(collection[currentIndex].title), line.size()));
对于这些数字,我们只需使用运算符&gt;&gt;读取数字即可。并忽略行尾的任何多余字符。同样,此操作可能会失败并进行检查。例如,这是价格:
my_stream >> collection[currentIndex].price;
my_stream.ignore();
if(!my_stream) {
break;
}
如果当前Book的所有成员都已正确读取,那么在循环体的末尾,我们将增加所看到的书籍数量(++currentIndex;
)。正如注释中所述,在readfile的上下文中没有必要显式关闭my_stream,因为当在范围的末尾调用my_stream的析构函数时,文件将被关闭。
一些额外的小问题。如上面评论中所讨论的,std :: string应该可以用于Book :: title,Book :: author和Book :: publisher。这是因为std :: string处理的情况是更加优雅地知道字符数,而不需要明确地管理内存。同样,集合将更适合于标准容器(例如,std :: vector)。这确实会导致indexBook的当前实现出现问题,可以将其更改为使用Book的复制构造函数存储在集合中。对于Book IO,运算符&gt;&gt;和运算符&lt;&lt;可以重载以替换和简化readfile和printCollection中的代码。
答案 1 :(得分:0)
本代码中有许多问题和不良习惯需要学习。
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++;
}
在C ++中应避免使用 memcpy
,而应依赖于内置于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做同样的事情。最后,除非您需要在for循环之外显示i
,否则可以将其设置为循环的本地。
因为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)
for循环中的while循环中调用i
,
3您不需要调整currentIndex
4(最糟糕的是)你滥用operator>>
。
在您的代码中,您可以编写以下内容:
for (i = 0; i < currentIndex; ++i) // *1
{
while (getline(my_stream, line)) // *2
{
cin >> line >> collection[i].title; // *3
因为您在main中未初始化的对象上调用indexBook
,currentIndex
为1,所以我们将进入for循环。
现在我们尝试将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);
}