Kompilere feil under lese / skrive størrelsen på flere structs til fil

stemmer
0

Jeg har allerede spurt 2 spørsmål slags knyttet til dette prosjektet, og jeg har kommet til denne konklusjonen. Skrive størrelsen på Struct til filen, og deretter lese den tilbake er den beste måten å gjøre dette.

Jeg oppretter et program for en hjemmelekse som vil tillate meg å opprettholde inventar. Jeg trenger å lese / skrive flere structs av samme type til en fil.

Problemet er ... dette er veldig komplisert, og jeg har problemer innpakning hodet mitt rundt hele prosessen. Jeg har sett en haug med eksempler og jeg prøver å sette det hele sammen. Jeg får kompilere feil ... og jeg har null peiling på hvordan du løser dem. Hvis du kunne hjelpe meg på dette jeg ville være så takknemlig ... takk. Jeg så mistet akkurat nå ...

**** forhåpentligvis SISTE EDIT # 3 *************

Min kode:

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

Mine Compile feil:

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

Det er den eneste feilen jeg får nå. Jeg ser koden din er så mye bedre. Min nye kompilatoren feil er SUPER lang. Jeg har vist hvor det feil peker på. Kan du hjelpe meg bare en siste gang?

Publisert på 02/04/2009 klokken 03:55
kilden bruker
På andre språk...                            


3 svar

stemmer
2

MERK: Dette er ikke et svar på kompilering feil du får, men heller et bredere syn på utholdenhet problemet du håndterer.

Serialisering og deserialization er ikke det enkleste problemet du kan arbeide på. Mitt råd vil være å investere i å lære biblioteker (boost :: serialisering) og bruke dem. De har allerede jobbet ut mange av problemene du vil møte på et eller annet tidspunkt. Pluss at de allerede har forskjellige formater (binære, xml, json ...)

Det første du må bestemme, det er hvis du bestemmer deg for å gå videre og implementere din egen, er hva som vil være den filformat og om det passer alle dine behov. Vil det alltid brukes i samme miljø? Vil plattformen endring (32 / 64bits)? Du kan velge å gjøre det binære som det er den enkleste, eller gjøre den lesbar for et menneske. Hvis du bestemmer deg på XML, JSON eller andre mer komplekse formater, bare glemme det og bruke et bibliotek.

Den enkleste løsningen er å jobbe på en binær fil, og det er også den løsningen som vil gi deg en minste fil. På den annen side, er det ganske fornuftig endringer arkitektur (si du migrerer fra en 32 til en 64 bits arkitektur / OS)

Etter å ha bestemt det formatet du trenger for å arbeide på den ekstra informasjon som ikke er en del av objektene nå, men må settes inn i filen for senere bruk. Deretter begynner å arbeide (og testing) fra de minste delene til mer komplekse elementer.

Et annet råd er å begynne å jobbe med det enkleste mest definerte del og bygge derfra. Begynne å unngå maler så mye som mulig, og når du har det klart og arbeider for en gitt datatype, arbeid på hvordan å generalisere det for en annen type.

Disclaimer: Jeg har skrevet koden direkte i nettleseren, så det kan være noen feil, skrivefeil eller bare om noe :)

Tekst

Den første enkle tilnærmingen er bare writting en tekstlig representasjon av teksten. Fordelen er at den er bærbar og kortere i kode (om ikke enklere) enn det binære tilnærming. De resulterende filene vil bli større, men brukeren lesbar.

På dette punktet må du vite hvordan lesing av tekst fungerer med iostreams. Spesielt når du forsøker å lese en streng systemet vil lese tegnene til den når en separator. Dette betyr at følgende kode:

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

vil bare lese opp til første plass, tab eller slutten av linjen. Når du leser tall (ints som et eksempel) vil systemet lese alle gyldige tall opp til den første ikke-gyldige siffer. Det er:

int i;
std::cin >> i;

med inngangs 12345a vil oppta alle tegn opp til 'a'. Du trenger å vite dette fordi det vil påvirke måten du fortsetter data for senere henting.

// 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

Så du trenger ganske mye separatorer for å sette i produksjon og å analysere input. For eksempel formål vil jeg velge '|' karakter. Det må være et tegn som ikke vises i tekstfeltene.

Det vil også være en god idé å ikke bare separate elementer, men også legge til litt ekstra info (størrelsen på vektoren). For elementene i vektoren kan du velge å bruke en annen separator. Hvis du ønsker å være i stand til å lese filen manuelt kan du bruke '\ n', slik at hvert element er i sin egen linje

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

For å lese, må du dele inn etter '\ n' for å få hvert element, og deretter med '|' å analysere 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;  
   }
}

I vektoren lesefunksjonen har jeg anvendt getline + analysere å lese heltall. Det er å garantere at neste getline () faktisk vil lese den første InventoryItem og ikke den etterfølgende '\ n' etter størrelse.

Den viktigste delen av koden er det 'parse' mal som er i stand til å konvertere fra en streng til en hvilken som helst type som har innsetting operatøren definert. Den kan brukes til å lese primitive typer, bibliotektyper (strengen, for eksempel), og brukertyper som har operatøren definert. Vi bruker den til å forenkle resten av koden ganske mye.

Binary

For et binært format, (ignorerer arkitektur, vil dette være en smerte i ræva hvis du overfører) den enkleste måten jeg kan tenke på er å skrive antall elemements i vektoren som en size_t (uansett størrelsen er i implementeringen) fulgt av alle elementer. Hvert element vil utskriften den binære representasjon av de forskjellige medlemmene. For grunnleggende typer som int, vil det bare utgang binært format av int. For strenger vil vi ty til writting en size_t nummer med antall tegn i strengen, etterfulgt av innholdet i strengen.

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 ] );
      }
   }
}

Jeg har valgt et annet navn for malen som skriver hovedtyper enn de funksjonene som skriver strenger eller InventoryItems. Årsaken er at vi ikke ønsker å senere ved en feiltakelse bruke malen til å skrive en kompleks type (dvs. Userinfo inneholder strenger) som vil lagre en feilaktig representasjon i disken.

Henting fra disk skal være ganske lik:

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

For å bruke denne tilnærmingen, må std :: istream og std :: ostream åpnes i binærmodus.

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

Alle feilsjekking er igjen som en øvelse for leseren :)

Hvis du har noen spørsmål om noen del av koden, bare spør.

Svarte 02/04/2009 kl. 07:21
kilden bruker

stemmer
2

Dine lese og skrive funksjoner er buggy. Spesielt bør du gjøre noe som dette i stedet:

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

OLD: bind1st krever at du tar med functionalfor at det skal fungere:

#include <functional>

I stedet for å håndtere alle disse funksjonene og slikt, men det ville være bedre å stole på iteratorer:

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

Du ønsker funksjoner for å lese og skrive din InventoryItemogså, sannsynligvis:

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;
}
Svarte 02/04/2009 kl. 04:20
kilden bruker

stemmer
1

EDIT: Prøver å rydde opp FUD:

bind1ster en del av STL s functionalspissen. STL eksisterte før boost dukket opp. Det er foreldet i C ++ 0x i favør av mer generisk versjon dvs. bind(aka boost::bind). Se vedlegg D.8 Binders for mer informasjon.

Nå det virkelige problemet (flere endringer kan gjøre dette ser dumt, men jeg vil holde dette for ettertiden skyld):

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

Dette er uakseptable linje. Dette forventer et longsom den andre parameteren, mens vectors size()er av typen size_teller unsigned intunder hetter.

Oppdater var det en skrivefeil: bruk size_tog ikke size_T:

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

Neste del:

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

Dette bør være structListeller en annen type. Også inkludere functionalå kunne bruke bind1st. Legg på toppen:

#include <functional>

Malen bind1sttar funktor. Passerer rundt vanlige funksjonspekere er ikke mulig uten noen andre hacks. Du kan bruke boost::bindsom et alternativ. Eller:

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

Nå for andre nitpicks:

Hva er:

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

Er du sikker på hva du bruker her? Hvis du ønsker STL strenger du må inkludere:

#include <string>

void main(void)

er ikke en standard signatur. Bruke en av:

int main(void)

eller

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

I / O er vanligvis mye enklere med de forhåndsdefinerte innsetting / ekstraksjons operatører. Du kan (og egentlig burde) bruke:

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

og tilsvar

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

Merk også din readFromFileog writeToFilefunksjoner må fikses å bruke vector<InventoryItem>i stedet for vectorrett og slett.

Svarte 02/04/2009 kl. 04:12
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more