2009-04-02 6 views
0

J'ai déjà posé 2 questions liées à ce projet, et j'ai atteint cette conclusion. Ecrire la taille de la structure dans le fichier, puis le relire est la meilleure façon de le faire.Compiler les erreurs lors de la lecture/écriture de la taille de plusieurs structures au fichier

Je crée un programme pour une tâche de devoirs qui me permettra de maintenir l'inventaire. J'ai besoin de lire/écrire plusieurs structures du même type dans un fichier. Le problème est que ... c'est vraiment compliqué et j'ai du mal à faire le tour du processus. J'ai vu un tas d'exemples et j'essaie de tout mettre ensemble. Je reçois des erreurs de compilation ... et je n'ai aucune idée sur la façon de les réparer. Si vous pouviez m'aider à ce sujet, je serais si reconnaissant ... merci. Je suis tellement perdu en ce moment ...

**** LE DERNIER EDIT VA BIEN # 3 *************

Mon code:

// Project 5.cpp : main project file. 

#include "stdafx.h" 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 
#include <algorithm> 

using namespace System; 
using namespace std; 
#pragma hdrstop 

int checkCommand (string line); 

template<typename Template> 
void readFromFile(Template&); 

template<typename Template> 
void writeToFile(Template&); 

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec); 

template<typename Template> 
void readVector(ifstream& in, vector<Template>& vec); 

struct InventoryItem { 
    string Item; 
    string Description; 
    int Quantity; 
    int wholesaleCost; 
    int retailCost; 
    int dateAdded; 
} ; 


int main(void) 
{ 
    cout << "Welcome to the Inventory Manager extreme! [Version 1.0]" << endl; 

    vector<InventoryItem> structList; 

    ofstream out("data.dat"); 

    writeVector(out, structList); 

    while (1) 
    { 

     string line = ""; 

     cout << endl; 
     cout << "Commands: " << endl; 
     cout << "1: Add a new record " << endl; 
     cout << "2: Display a record " << endl; 
     cout << "3: Edit a current record " << endl; 
     cout << "4: Exit the program " << endl; 
     cout << endl; 
     cout << "Enter a command 1-4: "; 

     getline(cin , line); 


     int rValue = checkCommand(line); 
     if (rValue == 1) 
     { 
      cout << "You've entered a invalid command! Try Again." << endl; 
     } else if (rValue == 2){ 
      cout << "Error calling command!" << endl; 
     } else if (!rValue) { 
      break; 
     } 
    } 


    system("pause"); 

    return 0; 
} 

int checkCommand (string line) 
{ 
    int intReturn = atoi(line.c_str()); 
    int status = 3; 

    switch (intReturn) 
    { 
     case 1: 
      break; 
     case 2: 
      break; 
     case 3: 
      break; 
     case 4: 
      status = 0; 
      break; 
     default: 
      status = 1; 
      break; 
    } 
    return status; 
} 

template <typename Template> 
void readFromFile(Template& t) 
{ 
    ifstream in("data.dat"); 
    readVector(in, t); Need to figure out how to pass the vector structList via a Template 
    in.close(); 
} 

template <typename Template> 
void writeToFile(Template& t) 
{ 
    ofstream out("data.dat"); 
    readVector(out, t); Need to figure out how to pass the vector structList via a Template 
    out.close(); 
} 

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec) 
{ 
    out << vec.size(); 

    for(vector<T>::const_iterator i = vec.begin(); i != vec.end(); ++i) 
    { 
     out << *i; // SUPER long compile error 
    } 
} 

template<typename T> 
vector<T> readVector(ifstream &in) 
{ 
    size_t size; 
    in >> size; 

    vector<T> vec; 
    vec.reserve(size); 

    for(int i = 0; i < size; ++i) 
    { 
     T tmp; 
     in >> tmp; 
     vec.push_back(tmp); 
    } 

    return vec; 
} 
Mon

des erreurs de compilation:

1>.\Project 5.cpp(128) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const InventoryItem' (or there is no acceptable conversion) 
1>  C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' 
1>  with 

C'est la seule erreur que je reçois maintenant. Je vois que votre code est tellement mieux. Ma nouvelle erreur de compilation est SUPER longue. J'ai montré où l'erreur pointe vers. Pourriez-vous m'aider juste une dernière fois?

+0

Je pense qu'il vaudrait mieux que les modifications au code original soient affichées en tant que réponses/suivis plutôt que de modifier la question. À la fin, les réponses originales seront complètement décalées et seront déroutantes pour les autres lecteurs. –

Répondre

2

Vos fonctions de lecture et d'écriture sont buggées. En particulier, vous devriez faire quelque chose comme ceci:

template<typename T> 
void write(ofstream &out, const T &t) 
{ 
    out << T; 
} 

VIEUX:bind1st exige que vous omettez functional pour que cela fonctionne:

#include <functional> 

Au lieu de faire face à toutes ces fonctions et ces , cependant, il serait préférable de compter sur les itérateurs:

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec) 
{ 
    out << vec.size(); 

    for(vector<T>::const_iterator i = vec.begin(); i != vec.end(); ++i) 
    { 
     out << *i; 
    } 
} 

template<typename T> 
vector<T> readVector(ifstream &in) 
{ 
    size_t size; 
    in >> size; 

    vector<T> vec; 
    vec.reserve(size); 

    for(int i = 0; i < size; ++i) 
    { 
     T tmp; 
     in >> tmp; 
     vec.push_back(tmp); 
    } 

    return vec; 
} 

Vous voudriez des fonctions à r Lire et écrire votre InventoryItem ainsi, probablement:

ostream &operator<<(ostream &out, const InventoryItem &i) 
{ 
    out << i.Item << i.Description; // FIXME Read/write strings properly. 
    out << i.Quantity; 
    out << i.wholesaleCost << i.retailCost; 
    out << i.dateAdded; 
} 

istream &operator>>(istream &out, InventoryItem &i) 
{ 
    // Keep in same order as operator<<(ostream &, const InventoryItem &)! 
    in >> i.Item >> i.Description; // FIXME Read/write strings properly. 
    in >> i.Quantity; 
    in >> i.wholesaleCost >> i.retailCost; 
    in >> i.dateAdded; 
} 
+0

Je ne sais pas vraiment ce que je fais. C'est incroyablement dur avec C++ ... oh mon dieu. Pouvez-vous m'aider à faire fonctionner ça? Je veux simplement pouvoir lire/écrire des structures dans un fichier friggin !!! – OneShot

+0

@OneShot, droite. Mes fonctions readVector et writeVector le feront. – strager

+0

@OneShot, Cependant, vous devez créer une fonction qui écrit InventoryItem lui-même. Je mettrai à jour ma réponse avec cette information. – strager

1

EDIT: Essayer d'éclaircir FUD:

bind1st fait partie d'en-tête de la STL functional. STL existait avant que le boost apparaisse. Il est déconseillé en C++ 0x en faveur de la version plus générique, c'est-à-dire bind (alias boost::bind). Voir l'Annexe D.8 Reliures pour plus d'informations.

Maintenant, le problème réel (plusieurs modifications peuvent faire ce regard stupide, mais je vais garder cela pour l'amour de la postérité):

write<long>(out, structList.size()); 

C'est la ligne incriminée. Cela attend un long comme deuxième paramètre, tandis que vectorsize() est de type size_t ou unsigned int sous les hottes.

Mise à jour il y avait une faute de frappe: utiliser size_t et non size_T:

write<size_t>(out, structList.size()); 

partie suivante:

for_each(structList.begin(), structList.end(), bind1st(write<InventoryItem>, out)); 

Cela devrait être structList ou un autre type. En outre, incluez functional pour pouvoir utiliser bind1st. Ajouter au sommet:

#include <functional> 

Le modèle bind1st prend un foncteur. Passer des pointeurs de fonction ordinaires n'est pas possible sans d'autres hacks. Vous pouvez utiliser boost::bind comme alternative.Ou:

for(InventoryItem::iterator i = structList.begin(), f = structList.end(); 
     i != f; ++i) 
    write<InventoryItem>(out, *i); 

maintenant pour d'autres nitpicks:

QU'EST-CE:

#include <String> 
... 
using namespace System; 

Êtes-vous sûr de ce que vous utilisez ici? Si vous voulez des chaînes STL, vous devez inclure:

#include <string> 

void main(void) 

n'est pas une signature standard. Utilisez un quelconque de:

int main(void) 

ou

int main(int argc, char *argv[]); 

I/O est en général beaucoup plus facile avec les opérateurs d'insertion/extraction prédéfinies. Vous pouvez (et devriez vraiment) utiliser:

istream is(...); 
is >> data; 

et de même

ostream os(...); 
os << data; 

Notez également vos fonctions readFromFile et writeToFile doivent être fixés à utiliser vector<InventoryItem> au lieu de vector simplement.

+0

Hey merci, pourriez-vous voir ce qui ne va pas avec mon code maintenant? – OneShot

+0

Un problème avec l'utilisation de << and >> est qu'il utilise foreach (voir ma réponse) et n'a pas créé de fonctions de lecture/écriture pour sa structure (voir ma réponse). J'aime cependant vos autres suggestions, même si elles ne se rapportent pas à la question ... – strager

+0

Les << and >> fonctionnent pour des types primitifs comme size_t - le point de la publication. – dirkgently

2

NOTE: Ce n'est pas une réponse aux erreurs de compilation que vous obtenez, mais plutôt une vision plus large du problème de la persistance que vous gérez.

La sérialisation et la désérialisation ne sont pas le problème le plus simple sur lequel vous pouvez travailler. Mon conseil serait d'investir dans l'apprentissage des bibliothèques (boost :: sérialisation) et de les utiliser. Ils ont déjà résolu de nombreux problèmes auxquels vous serez confrontés à un moment ou à un autre. De plus, ils ont déjà différents formats de sortie (binaire, xml, json ...)

La première chose que vous devez décider, c'est que si vous décidez d'aller de l'avant et de mettre en œuvre votre propre, quel sera le format de fichier et si cela convient à tous vos besoins. Sera-t-il toujours utilisé dans le même environnement? La plateforme va-t-elle changer (32/64bits)? Vous pouvez décider de le rendre binaire car c'est le plus simple, ou le rendre lisible pour un être humain. Si vous décidez de XML, JSON ou d'autres formats plus complexes, il suffit de l'oublier et d'utiliser une bibliothèque.

La solution la plus simple est de travailler sur un fichier binaire et c'est aussi la solution qui vous donnera le plus petit fichier.D'un autre côté, il est très sensible aux changements d'architecture (disons que vous migrez d'une architecture/OS de 32 à 64 bits)

Après avoir décidé du format, vous devrez travailler sur les informations supplémentaires qui ne font pas partie de votre objets maintenant mais doit être inséré dans le fichier pour une récupération ultérieure. Puis commencez à travailler (et à tester) des plus petites parties à des éléments plus complexes.

Un autre conseil serait de commencer à travailler avec la partie la plus simple et la plus définie à partir de là. Commencez par éviter autant que possible les modèles, et une fois que vous l'avez clairement défini et que vous travaillez pour un type de données donné, travaillez sur la manière de le généraliser pour tout autre type.

Disclaimer: J'ai écrit le code directement sur le navigateur, donc il pourrait y avoir des erreurs, des fautes de frappe ou à peu près tout :)

Texte

La première approche simple est juste un texte ÉCRITURE représentation du texte. L'avantage est qu'il est portable et plus court en code (sinon plus simple) que l'approche binaire. Les fichiers résultants seront plus grands mais lisibles par l'utilisateur.

À ce stade, vous devez savoir comment lire du texte avec iostreams. En particulier, chaque fois que vous essayez de lire une chaîne, le système lira les caractères jusqu'à ce qu'il atteigne un séparateur. Cela signifie que le code suivant:

std::string str; 
std::cin >> str; 

lit uniquement le premier espace, la première tabulation ou la fin de la ligne. Lors de la lecture des nombres (ints par exemple), le système lira tous les chiffres valides jusqu'au premier chiffre non valide. C'est-à-dire:

int i; 
std::cin >> i; 

avec l'entrée 12345a consommera tous les caractères jusqu'à 'a'. Vous devez le savoir car cela influera sur la façon dont vous persistez à récupérer des données pour une récupération ultérieure.

// input: "This is a long Description" 
std::string str; 
std::cin >> str; // Will read 'This' but ignore the rest 

int a = 1; 
int b = 2; 
std::cout << a << b; // will produce '12' 
// input: 12 
int read; 
std::cint >> read; // will read 12, not 1 

Vous avez donc besoin de séparateurs à insérer dans la sortie et d'analyser l'entrée. À titre d'exemple, je vais sélectionner le '|' personnage. Ce doit être un caractère qui n'apparaît pas dans les champs de texte.

Ce sera également une bonne idée non seulement de séparer les éléments mais aussi d'ajouter des informations supplémentaires (taille du vecteur). Pour les éléments du vecteur, vous pouvez décider d'utiliser un séparateur différent. Si vous voulez être en mesure de lire le fichier manuellement, vous pouvez utiliser « \ n » de sorte que chaque article se trouve dans sa propre ligne

namespace textual { 
    std::ostream & operator<<(std::ostream& o, InventoryItem const & data) 
    { 
     return o << data.Item << "|" << data.Description << "|" << data.Quantity 
     << "|" << data. ...; 
    } 
    std::ostream & operator<<(std::ostream & o, std::vector<InventoryItem> const & v) 
    { 
     o << v.size() << std::endl; 
     for (int i = 0; i < v.size(); ++i) { 
     o << v[i] << std::endl; // will call the above defined operator<< 
     } 
    } 
} 

Pour la lecture, vous devez diviser l'entrée par « \ n » à obtenir chaque élément, puis avec '|' pour analyser le InventoryItem:

namespace textual { 
    template <typename T> 
    void parse(std::string const & str, T & data) 
    { 
     std::istringstream st(str); // Create a stream with the string 
     st >> data; // use operator>>(std::istream 
    } 

    std::istream & operator>>(std::istream & i, InventoryItem & data) 
    { 
     getline(i, data.Item, '|'); 
     getline(i, data.Description, '|'); 

     std::string tmp; 
     getline(i, tmp, '|'); // Quantity in text 
     parse(tmp, data.Quantity); 
     getline(i, tmp, '|'); // wholesaleCost in text 
     parse(tmp, data. wholesaleCost); 
     // ... 
     return i; 
    } 

    std::istream & operator>>(std::istream & i, std::vector<InventoryItem> & data) 
    { 
     int size; 

     std::string tmp; 
     getline(i, tmp); // size line, without last parameter getline splits by lines 
     parse(tmp, size); // obtain size as string 

     for (int i = 0; i < size; ++i) 
     { 
     InventoryItem data; 
     getline(i, tmp); // read an inventory line 
     parse(tmp, data); 
     }  
     return i; 
    } 
} 

Dans la fonction de lecture vectorielle J'ai utilisé getline + analyser pour lire l'entier. Cela permet de garantir que le prochain getline() lira réellement le premier InventoryItem et non le \ n 'final après la taille.

Le morceau de code le plus important est le modèle 'parse' qui est capable de convertir d'une chaîne à n'importe quel type qui a l'opérateur d'insertion défini. Il peut être utilisé pour lire les types primitifs, les types de bibliothèques (chaîne, par exemple) et les types d'utilisateurs ayant l'opérateur défini. Nous l'utilisons pour simplifier un peu le reste du code.

binaire

Pour un format binaire, (abstraction faite architecture, ce sera une douleur dans le cul si vous migrez) la façon la plus simple que je peux penser est écrit le nombre de elemements dans le vecteur comme size_t (quelle que soit la taille de votre implémentation), suivi de tous les éléments. Chaque élément imprimera la représentation binaire de chacun de ses membres. Pour les types de base comme int, il sortira simplement le format binaire de l'int. Pour les chaînes, nous aurons recours à l'écriture d'un nombre size_t avec le nombre de caractères dans la chaîne suivi du contenu de la chaîne.

namespace binary 
{ 
    void write(std::ofstream & o, std::string const & str) 
    { 
     int size = str.size(); 
     o.write(&size, sizeof(int)); // write the size 
     o.write(str.c_str(), size); // write the contents 
    } 
    template <typename T> 
    void write_pod(std::ofstream & o, T data) // will work only with POD data and not arrays 
    { 
     o.write(&data, sizeof(data)); 
    } 
    void write(std::ofstream & o, InventoryItem const & data) 
    { 
     write(o, data.Item); 
     write(o, data.Description); 
     write_pod(o, data.Quantity); 
     write_pod(o, data. ... 
    } 
    void write(std::ofstream & o, std::vector<InventoryItem> const & v) 
    { 
     int size = v.size(); 
     o.write(&size, sizeof(size)); // could use the template: write_pod(o, size) 
     for (int i = 0; i < v.size(); ++i) { 
     write(o, v[ i ]); 
     } 
    } 
} 

J'ai choisi un nom différent pour le modèle qui écrit que les types de base des fonctions qui écrivent des chaînes ou InventoryItems. La raison en est que nous ne voulons plus utiliser par erreur le modèle pour écrire un type complexe (c'est-à-dire des chaînes contenant UserInfo) qui stockera une représentation erronée sur le disque.

récupération à partir du disque devrait être assez similaire:

namespace binary { 
    template <typename T> 
    void read_pod(std::istream & i, T& data) 
    { 
     i.read(&data, sizeof(data)); 
    } 
    void read(std::istream & i, std::string & str) 
    { 
     int size; 
     read_pod(i, size); 
     char* buffer = new char[size+1]; // create a temporary buffer and read into it 
     i.read(buffer, size); 
     buffer[size] = 0; 
     str = buffer; 
     delete [] buffer; 
    } 
    void read(std::istream & i, InventoryItem & data) 
    { 
     read(i, data.Item); 
     read(i, data.Description); 
     read(i, data.Quantity); 
     read(i, ... 
    } 
    void read(std::istream & i, std::vector<InventoryItem> & v) 
    { 
     v.clear(); // clear the vector in case it is not empty 

     int size; 
     read_pod(i, size); 
     for (int i = 0; i < size; ++i) 
     { 
     InventoryItem item; 
     read(i, item); 
     v.push_back(item); 
     } 
    } 
} 

Pour utiliser cette approche, le std :: istream et std :: ostream doit être ouvert en mode binaire.

int main() 
{ 
    std::ifstream persisted("file.bin", ios:in|ios::binary); 
    std::vector<InventoryItem> v; 
    binary::read(persisted, v); 

    // work on data 

    std::ofstream persist("output.bin", ios::out|ios::binary); 
    binary::write(persist, v); 
} 

tous les contrôles d'erreur est laissé comme un exercice pour le lecteur :)

Si vous avez une question sur une partie du code, il suffit de demander.

+0

Wow, je viens de le trouver. C'est tout simplement incroyable. Vous n'aviez pas à le faire mais vous avez pris le temps de le faire. Tout ce que je peux faire est de dire merci. mais sachez que je le pense vraiment. – OneShot

Questions connexes