Gå til innhold

Lagre en map<string,int>


Anbefalte innlegg

Forsøker å lage en enkel telefonbok.

Den utnytter map fra stl (standard template libriary).

Problemet mitt er at jeg ikke klarer å lagre denne (binært).

Kode

Vet allerede hvordan jeg skal behandle tekst-filer, men sliter med binært.

 

Det er mulig at map lagrer sine data i det frie lageret.

 

std::map<std::string, int> blokk;

/* globalt objekt som skal hentes ved oppstart og lagres ved avslutning. */

Lenke til kommentar
Videoannonse
Annonse

Du kan ikke lagre std::map objektet binært slik du prøver. std::map allokerer på heap og har derfor interne pekere som den holder styr på. Å skrive en pekeradresse til fil har lite for seg..

 

En måte å løse oppgaven din på er å lage en PhoneBook klasse som du implementerer operator>> og operator<< til. Du vil også få en litt mer oversiktlig kode og jobbe med.

 

Her er et lite eksempel jeg skrev i hui og hast:

 

#include <iostream>
#include <fstream>
#include <string>
#include <map>


using std::cout;
using std::endl;


class PhoneBook
{
   typedef std::map<std::string, int> PhoneBookMapType;
public:

   void AddEntry(const std::string & name, int number)
   {
       m_book.insert( std::make_pair(name, number) );
   }

   void RemoveAllEntries()
   {
       m_book.clear();
   }

   friend std::ostream & 
       operator<< (std::ostream & ostr, const PhoneBook & phoneBook)
   {
       PhoneBookMapType::const_iterator it;

       for(it=phoneBook.m_book.begin(); it!=phoneBook.m_book.end(); ++it)
       {
           ostr << it->first << endl << it->second << endl;
       }

       return ostr;
   }

   friend std::istream &
       operator>> (std::istream & istr, PhoneBook & phoneBook)
   {
       std::string name;
       int number;

       while(!istr.eof())
       {
           istr >> name;
           istr >> number;
           phoneBook.AddEntry(name, number);
       }

       return istr;
   }


private:
   PhoneBookMapType m_book;
};


int main()
{
   PhoneBook book;

   // add some entries
   book.AddEntry("Britney", 555666666);
   book.AddEntry("Christina", 555123123);
   book.AddEntry("Beyonce", 555111111);
   book.AddEntry("Charlize", 555394837);

   // export phone book to file
   std::ofstream out("telefon.bin", std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
   out << book;
   out.close();
   
   // remove entries from memory
   book.RemoveAllEntries();
   
   // read entries back from file
   std::ifstream in("telefon.bin", std::ios_base::in | std::ios_base::binary);
   in >> book;
   in.close();

   // print entries to console
   cout << book << endl;
}

Lenke til kommentar

Du bør aldri lagre objekter direkte. Unntaket er enkle datastructer som kun holder data (ikke pekere selvfølgelig..). Men dette kan også introdusere en del problemer hvis filene skal brukes på forskjellige kompilatorer og platformer. Tenker blant annet på alignering og byterekkefølge (eng. "byte order").

 

Et lite eksempel:

 

Vi har en struct som består av to tall, en unsigned short og en unsigned int. De er henholdsvis 16 bits = 2 bytes og 32 bits = 4 bytes i Windows. Vi lager et par typedefs for å gjøre koden mer tydelig:

typedef unsigned short Uint16;
typedef unsigned int Uint32;

struct MyStruct
{
   Uint16 size;
   Uint32 data;
};

Ut fra vår logikk bør størrelsen på et MyStruct objekt nå være 16 + 32 = 48 bits = 6 bytes. Men hvis du prøver å skrive ut størrelsen vil du kanskje få deg en overraskelse?

cout << sizeof(MyStruct) << " bytes" << endl;

Resultatet blir 8! Hvis du ikke har hørt om minnealignering (eng. "memory alignment"), vil du nok kanskje klø deg litt i hue. Men dette har noe med hvordan en 32-bits PC-prosessor jobber. Den jobber best med 32-bits heltall alignert på 32-bits addresser (dagens PC'er er stort sett 32-bits). Kompilatoren gjør derfor det som er mest effektivt og fyller ut de resterende to bytes i den første size variablen med "dummy" verdier. Dvs. at size i virkeligheten tar opp 4 bytes selv om den strengt tatt bare trenger 2.

 

La oss gå ut fra følgende struct:

MyStruct obj = {0, 0xffffffff};

obj vil se slik ut i minnet:

 

0x00 0x00 (0xCC 0xCC) // size (verdier i parantes er "padding")

0xff 0xff 0xff 0xff // data

 

Du vil se dette tydelig hvis du prøver å lagre obj til en fil og åpne den i en hex-editor:

MyStruct obj= {0, 0xffffffff};
std::ofstream o("data.bin", std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
o.write((const char*)&obj, sizeof(data));
o.close();

 

Dette er ikke noe problem så lenge du lagrer og henter data fra kun en og samme applikasjon på en platform. Men når du prøver å bruke filen spesielt på en annen platform kan du støte på problemer. Dessuten er det jo heller mindre lurt å kaste bort to bytes for hvert objekt du ønsker å lagre til filen.

 

Du kan tvinge de fleste kompilatorer til å unngå "padding", men det er egentlig en softwareemulering som gjør koden din mindre effektiv (selv om du sannsynligvis ikke vil merke noe forskjell).

 

Du må også lese litt om "byte ordering" hvis du ønsker å lage et filformat som er kompatibelt med big endian og little endian platformer (søk på google).

 

Tilbake til eksemplet:

 

Hvis du absolutt ønsker å lagre ditt eksempel binært, kan du legge til følgende to medlemsfunksjoner i PhoneBook klassen:

friend std::ofstream & 
   operator<< (std::ofstream & ostr, const PhoneBook & phoneBook)
{
   PhoneBookMapType::const_iterator it;

   for(it=phoneBook.m_book.begin(); it!=phoneBook.m_book.end(); ++it)
   {
       ostr << it->first << '\n';
       ostr.write((const char*)&it->second, sizeof(int));
   }

   return ostr;
}

friend std::ifstream &
   operator>> (std::ifstream & istr, PhoneBook & phoneBook)
{
   std::string name;
   int number;

   while(!istr.eof())
   {
       istr >> name;
       istr.seekg(1, std::ios_base::cur); // skip '\n'
       istr.read((char*)&number, sizeof(number));
       phoneBook.AddEntry(name, number);
   }

   return istr;
}

 

Du kan jo prøve å sammenligne filene du får med disse funksjonene og det første eksemplet.

 

Det får være nok for i dag :) Ble en "litt" lengre post enn jeg hadde tenkt, men jeg fikk et lite tastekick i den lettbrisne ølrusen ;)

Endret av kjetil7
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...