2016-07-26 1 views
0

J'essaie de faire une sérialisation avec fstream. La syntaxe du flux est la suivante: "IndexLengthDataIndexLengthData ...". par exemple, 11c22cc33ccc. Lors de la lecture du fichier, le flux d'entrée indiquera "11" dans son ensemble pour l'index.faire une sérialisation avec C++ fstream

L'index est compris entre [1, INT_MAX]. La longueur est limitée à 516.

Est-ce que je peux faire cela sans utiliser un séparateur.e.g, "@" ou "#", entre index et longueur?

int main() { 
    std::ofstream ofs; 
    ofs.open("myfile.txt", std::ofstream::out | std::ofstream::trunc); 
    for(int i = 1; i <= 10; ++i) { 
    ofs << i; // for index 
    ofs << i; // for length 
    for (int j = 0; j < i; ++j) ofs << 'c'; 
    } 
    ofs.close(); 
    std::ifstream ifs; 
    ifs.open("myfile.txt", std::ifstream::in); 
    for (int i = 0; !ifs.eof() && ifs.good(); ++i) { 
    int index = 0, length = 0; 
    ifs >> index; 
    ifs >> length; 
    std::cout << "index is " << index << "length is " << length << std::endl; 
    // Jump to the next entry 
    ifs.seekg(length, std::ios_base::cur); 
    } 
} 
+0

Seulement si votre index est seulement un seul chiffre. Vous avez besoin d'un index de longueur fixe et d'un champ de longueur de longueur fixe, puis remplissez les données avec des zéros. –

+0

non, l'index n'est pas un seul chiffre. ce pourrait être une valeur de tout entier supérieur à 0. – pepero

+2

Si 'IndexLength' contient' 1234', quelle est la valeur de 'Index' et la valeur de' Length': 1, 234 ou 12, 34 ou 123, 4? –

Répondre

2

Oui, si vous avez le formatage de taille fixe, donc 10 caractères pour index, 3 caractères pour la longueur, et votre exemple serait encodée comme:
" 1 1c 2 2cc 3 3ccc".

Vous parlez également de fstream, mais il semble que vous recherchiez une sérialisation texte (lisible par l'homme), pas une binaire. Si tel est le cas, mais que vous n'avez pas besoin d'une forme réellement lisible, vous pouvez marquer le premier octet de longueur avec un peu (les chiffres en ASCII sont codés comme 0x30 à 0x39, donc vous pouvez par exemple définir 0x40 bit sans détruire les données . octets Ensuite, votre exemple ressemblerait à ceci:
1qc2rcc3sccc (q = 0x71 = 0x40|0x31 = 0x40|'1')

pour une valeur plus il regarderait que: 113q0 ... ARGH Je voulais sérialisation 10 caractères longue chaîne "", et regardez ce qui s'est passé, j'ai eu la longueur 100 au lieu de 10 (ou pire encore 10, si vous ne voulez pas limiter), le début et la fin de la longueur doivent être altérés d'une manière ou d'une autre, en utilisant éventuellement le bit 0x80 pour marquer la fin de la longueur.
1\361c2\362cc3\363ccc (\361 = 0xF1 = 0x40|0x80|0x31 = 0x40|0x80|'1')

valeur plus longue deuxième essai:
113q° (indice 113, la longueur 10, les données "", q = 0x40|'1', ° = 0x80|'0').

Vous ne voulez pas plutôt la forme binaire? Serait plus court.


BTW, si cela ne vous dérange pas les valeurs entachant, mais vous voulez rester en ASCII 7 bits, vous pouvez entacher ne démarre pas et à la fin de la longueur, mais les extrémités des deux index et la longueur, et seulement avec 0x40. Donc le 11c deviendrait qqc. Et 11310 serait 11s1p.


écriture binaire/lecture avec la plate-forme agnostique ENDIANNESS (ce fichier écrit sur little-endian travaillera sur d'autres plate-forme avec big-endian).

#include <iostream> 
#include <cstdint> 
#include <vector> 

/** 
* Writes index+length+data in binary form to "out" stream. 
* 
* Returns number of bytes written to out stream. 
* 
* Does no data validation (the variable types are only limits for input data). 
* 
* writeData and readData are done in endiannes agnostic way. 
* So file saved at big-endian platform will be restored correctly on little-endian platform. 
**/ 
size_t writeData(std::ostream & out, 
     const uint32_t index, const uint16_t length, const uint8_t *data) { 
    // Write index and length bytes to out stream, resolve endiannes of host platform. 
    out.put((char)((index>>0)&0xFF)); 
    out.put((char)((index>>8)&0xFF)); 
    out.put((char)((index>>16)&0xFF)); 
    out.put((char)((index>>24)&0xFF)); 
    out.put((char)((length>>0)&0xFF)); 
    out.put((char)((length>>8)&0xFF)); 
    // If any data, write them to stream 
    if (0 < length) out.write(reinterpret_cast<const char *>(data), length); 
    return 4 + 2 + length; 
} 

/** 
* Read data from stream "in" stream into variables index, length and data. 
* 
* If "in" doesn't contain enough bytes for index+length, zero index/length is returned 
* 
* If "in" contains more than index+length bytes, but the data are shorter than length, 
* then "repaired" shorter data are returned with shorter "length" (not the read one). 
**/ 
void readData(std::istream & in, 
     uint32_t & index, uint16_t & length, std::vector<uint8_t> & data) { 
    // clear current values in index, length, data 
    index = length = 0; data.clear(); 
    // read index+length header from stream 
    uint8_t buffer[6]; 
    in.read(reinterpret_cast<char *>(buffer), 6); 
    if (6 != in.gcount()) return; // header data (index+legth) not found 
    // Reassemble read bytes together to index/length numbers in host endiannes. 
    index = (buffer[0]<<0) | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); 
    length = (buffer[4]<<0) | (buffer[5]<<8); 
    if (0 == length) return; // zero length, nothing more to read 
    // Read the binary data of expected length 
    data.resize(length); // reserve memory for read 
    in.read(reinterpret_cast<char *>(data.data()), length); 
    if (length != in.gcount()) { // data read didn't have expected length, damaged file? 
     // TODO you may want to handle damaged data in other way, like returning index 0 
     // This code will simply accept shorter data, and "repair" length 
     length = in.gcount(); 
     data.resize(length); 
    } 
} 

Pour le voir en action, vous pouvez l'essayer sur cpp.sh.

+1

Je me demande pourquoi ne pas simplement ajouter un séparateur (espace) ... donc vous auriez '1 1 c2 2 cc3 3 ccc'. Assurez-vous simplement de lire les données du contenu complet, s'il y aurait une chaîne avec un espace inclus, comme '56 11 hello world'. – Ped7g

+0

oui, je veux juste économiser de l'espace. il n'est pas nécessaire d'être un fichier texte. le format binaire est ok. il semble que ces – pepero

+0

@pepero: btw, cette réponse est un peu "moqueuse" de vous. Cela n'a pas trop de sens pour ce que vous demandez. Si vous voulez économiser de l'espace, ajoutez simplement la bibliothèque 7zip à votre projet et lancez la compression du flux (bien que cela puisse sembler un peu complexe pour vous, mais c'est une histoire différente, combien d'efforts et d'apprentissage vous allez mettre dans il). Et je voudrais aller pour la sérialisation binaire, si la sortie n'a pas besoin d'être lisible par l'homme et l'index, la longueur est souvent plus longue que 1-2chars (32b int est 4B de long). Donc, cette réponse est juste show-of, que cela ** peut ** être fait sans séparateur. – Ped7g