2009-09-30 5 views
0

J'ai une famille de classes qui contient seulement des variables des types suivants: std :: string, int, double. Je devrais être en mesure de sérialiser/désérialiser les objets de ces classes vers/à partir de la chaîne C (null terminé). Je ne veux pas utiliser un sérialiseur 3rdparty et je ne veux pas écrire un sérialiseur complet par moi-même. Je sérialiserai/désérialiser seulement une fois dans mon code. Alors, comment écrire un sérialiseur très très petit et élégant et le faire assez rapidement?Sérialiseur minuscule

MISE À JOUR

J'ai écrit et testé mon propre. Peut-être que ce sera utile pour quelqu'un. Si vous remarquez des bugs ou si vous avez des suggestions pour le rendre meilleur, faites le moi savoir. Ici, il est:

typedef std::ostringstream ostr; 
    typedef std::istringstream istr; 

    const char delimiter = '\n'; 
    const int doublePrecision = 15; 

    void Save(ostr& os, int x) { os << x << delimiter; } 

    void Save(ostr& os, double x) 
    { 
    os.precision(doublePrecision); 
    os << x << delimiter; 
    } 

    void Save(ostr& os, const std::string& x) { os << x << delimiter; } 

    void Load(istr& is, int& x) 
    { 
    is >> x; 
    is.rdbuf()->sbumpc(); // get rid of delimiter 
    } 

    void Load(istr& is, double& x) 
    { 
    is >> x; 
    is.rdbuf()->sbumpc(); // get rid of delimiter 
    } 

    void Load(istr& is, std::string& x) { getline(is, x, delimiter); } 

Test:

 std::string a = "Test string 1 2 and 2.33"; 
    std::string b = "45"; 
    double c = 45.7; 
    int d = 58; 
    double e = 1.0/2048; 

    std::ostringstream os; 
    Save(os, a); 
    Save(os, b); 
    Save(os, c); 
    Save(os, d); 
    Save(os, e); 
    std::string serialized = os.str(); 

    std::string aa; 
    std::string bb; 
    double cc = 0.0; 
    int dd = 0; 
    double ee = 0.0; 

    std::istringstream is(serialized); 
    Load(is, aa); 
    Load(is, bb); 
    Load(is, cc); 
    Load(is, dd); 
    Load(is, ee); 

    ASSERT(a == aa); 
    ASSERT(b == bb); 
    ASSERT(c == cc); 
    ASSERT(d == dd); 
    ASSERT(e == ee); 
+0

Qu'en est-chaînes avec « \ n » en eux? Il existe des codes ascii spécifiques pour la fin du groupe/enregistrement/bloc, etc. qui ne sont pas susceptibles d'apparaître dans une chaîne. –

Répondre

1

Il y a 2 façons de sérialisation données de chaîne à un flux, vous pouvez soit le faire C-style et utiliser une valeur nulle pour y mettre fin ou (plus portably et plus facile à lire) d'abord en sortie un octet qui indique combien de temps la chaîne est ensuite écrire la chaîne. Maintenant, si vous voulez différencier une chaîne d'une non-chaîne (numéro dans ce cas), vous pouvez ajouter chaque "paquet" (item) à un octet, disons 0x00 pour int, 0x01 pour double, 0x02 pour chaîne, et branchez un interrupteur en fonction de ce que le code est. De cette façon, vous pouvez même écrire l'int/double comme un octet, de sorte que vous ne perdrez pas de précision et vous finirez avec un fichier plus petit/plus facile à lire.

+0

Je n'ai pas besoin de différencier les types. C'est ok si je dois le savoir pour lire exactement. La question est de savoir comment sérialiser/désérialiser une chaîne dans/depuis un flux. Merci – bocco

+0

C'est la chose, le code lui-même est aussi facile que 'os << tobin (x.length()) << x', où' tobin (int) 'écrit un nombre comme binaire. Mais une fois que vous atteignez ce point, vous devez différencier les chaînes (nombre + tableau de char) et les nombres réels. Et vous faites cela en divisant votre flux en paquets et chaque paquet reçoit un "code" qui identifie le type de paquet. – Blindy

+0

Oui, j'utilise '\ n' pour l'instant, mais je n'ai pas besoin de différencier les types. – bocco

0
void save(std::ostringstream& out, const std::string& x) 
{ 
    out << x; 
} 


void read(std::istringstream& in, std::string& x) 
{ 
    in.str(x); 
} 

de here.

+0

Que se passe-t-il si je sérialise séquentiellement deux chaînes? Comment cela va-t-il le reconnaître pendant la désérialisation? – bocco

+0

Vous devez les séparer d'une certaine manière, peut-être en ajoutant endl (out << x << endl;) ou un autre caractère échappé. – luvieere

+0

in.str (x) ne lit pas le flux. Il réinitialise le contenu du flux à x. – bocco

0

Je sais que vous ne voulez pas utiliser un sérialiseur 3rdParty, mais si vous voulez reconsidérer: utilisez Boost.Serialization.

(même si ce n'est pas la réponse pour vous, il est peut-être pour quelqu'un trébucher autre sur cette question)

exemple très simple

class some_data 
{ 
    public: 
    template<class Archive> 
    void serialize(Archive & ar, const unsigned int version) 
    { 
     ar & my_string; 
     ar & my_double; 
    } 

    private: 
    std::string my_string; 
    double my_double; 
}; 

puis pour sauver:

my_data dataObject; 

std::ofstream ofs("filename"); 
boost::archive::text_oarchive oa(ofs); 
oa << dataObject; 

ou pour charger:

my_data dataObject; 

std::ifstream ifs("filename"); 
boost::archive::text_iarchive ia(ifs); 
ia >> dataObject; 
+1

En fait, je recommanderais plutôt le protocole Protocols Buffer de Google. Meilleure syntaxe et performances (taux de compression/temps). –

+0

Merci pour Protocols Buffer de Google. Ce n'est pas pour cette tâche, mais ce sera très très utile! – bocco

0

Voir le format sun xdr. C'est binaire et efficace.

J'ai une petite classe personnalisée pour ce faire, à titre d'exemple:

Marshall& Marshall::enc(const string& str) { 
    size_t size = str.size(); 
    size_t pad = (4 - (size%4))%4; 
    size_t size_on_buff = size + pad; 
    space_for(sizeof(uint32_t) + size + pad); 
    check_size_t_overflow(size); 
    enc(static_cast<uint32_t>(size)); 
    // xdr mandates padding 
    //space_for(size_on_buff); 
    memcpy(&(*buff)[pos],str.data(), size); 
    memset(&(*buff)[pos+size],0,pad); 
    pos+=size_on_buff; 
    return *this; 
} 

Marshall& Marshall::dec(string& str) { 
    str.clear(); 
    size_t size; 
    dec(size); 
    size_t pad = (4 - (size%4))%4; 
    size_t size_on_buff = size + pad; 
    ck_space_avl(size + pad); 
    //str.resize(size); 
    str.assign((char*)&(*buff)[pos],size); 
    pos+=size_on_buff; 
    return *this; 
} 
Questions connexes