2016-09-14 3 views
0

Я должен создать программу, которая индексирует книги в моей коллекции. Структура содержит обычную информацию о книге: название, автор, издатель и т. Д. Однако я не получаю выход. Одна из проблем заключается в том, что названия будут иметь пробелы.Добавление содержимого файла в структуру

/* 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; 
     } 
    } 

Error in output

+5

Если вы используете C++, вам следует действительно использовать 'std :: string' вместо массивов char. –

+2

Поиск в Интернете для «stackoverflow C++ read file struct» для примеров того, как читать файл данных в структуру. Слишком много подобных вопросов и ответов уже. –

+0

Подсказка: похоже, что вы только увеличиваете 'currentIndex' когда-либо, независимо от того, сколько книг находится в вашем txt-файле. –

ответ

1

Основными проблемами являются ReadFile с функцией. Как указано в комментариях, std :: fstream может принимать строку в качестве первого аргумента, поэтому вызов open later не требуется. Кроме того, следует проверить, открыт ли файл перед выполнением операций над ним. Строка std :: string не требуется инициализировать для последующего использования в этой функции. Это приводит к следующему коду.

fstream my_stream("input.txt"); 
if(!my_stream) { 
    return; 
} 
string line; 

Петли readfile, похоже, не построены правильно. Внешний цикл увеличивается по диапазону текущих индексов для массива сбора, в то время как внутренний цикл выглядит так, как будто предназначен для чтения всего файла. Если внутренний цикл был построен правильно, то содержимое файла будет записано в 0-ю книгу в коллекции. Внутренний цикл начинается с тестирования, если чтение строки из файла, которое игнорируется путем чтения ввода из std :: cin, выполняется успешно.

Поскольку количество «книг» в input.txt неизвестно, внешний цикл, по-видимому, не имеет отношения к цели чтения всех «книг» в другой элемент коллекции. Чтобы прочитать весь файл, мы изменим цикл, чтобы проверить, что файл по-прежнему доступен для чтения.

while(my_stream) { 

Для чтения строк, мы должны читать и хранить всю строку, используя зЬй :: GetLine (my_stream >> line; здесь будет получить подножку пробелов), а тетср скопировать содержимое строки в соответствующем char. Поскольку std :: getline может выйти из строя, мы проверяем успех, прежде чем использовать содержимое строки. Например, вот название:

// title 
if(!std::getline(my_stream, line)) { 
    break; 
} 
memcpy(collection[currentIndex].title, line.c_str(), std::min(sizeof(collection[currentIndex].title), line.size())); 

Для чисел, мы можем только читать числа, используя оператор >> и игнорировать любые дополнительные символы в конце строки. Аналогично, эта операция может быть неудачной и также проверена. Например, вот цена:

my_stream >> collection[currentIndex].price; 
    my_stream.ignore(); 
    if(!my_stream) { 
     break; 
    } 

Если все члены текущей книги были правильно читать, то в конце тела цикла, мы увеличиваем количество книг видно (++currentIndex;). Как указано в комментариях, явное закрытие my_stream не обязательно в контексте readfile, так как файл будет закрыт, когда деструктор для my_stream вызывается в конце области.

Некоторые дополнительные незначительные баллы. Как обсуждалось выше в комментариях, std :: string следует, вероятно, использовать для книг :: title, Book :: author и Book :: publisher. Это связано с тем, что std :: string обрабатывает случай, когда количество символов неизвестно более изящно, не прибегая к управлению памятью явно. Аналогично, сбор лучше всего подходит для стандартного контейнера (например, std :: vector). Это вызывает проблемы для текущей реализации indexBook, которые можно изменить, чтобы использовать конструктор копирования книги для хранения в коллекции.Для книги IO оператор >> и оператор < < могут быть перегружены для замены и упрощения кода внутри файла readfile и printCollection.

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++; 
} 

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); 
} 
Смежные вопросы