2009-12-06 6 views
2

Je tente un simple test d'E/S de fichier binaire en utilisant l'algorithme de copie STL pour copier des données vers/depuis des conteneurs et un binaire fichier. Voir ci-dessous:E/S de fichiers binaires C++ vers/depuis des conteneurs (autres que char *) en utilisant des algorithmes STL

1 #include <iostream> 
2 #include <iterator> 
3 #include <fstream> 
4 #include <vector> 
5 #include <algorithm> 
6 
7 using namespace std; 
8 
9 typedef std::ostream_iterator<double> oi_t; 
10 typedef std::istream_iterator<double> ii_t; 
11 
12 int main() { 
13 
14 // generate some data to test 
15 std::vector<double> vd; 
16 for (int i = 0; i < 20; i++) 
17 { 
18  double d = rand()/1000000.0; 
19  vd.push_back(d); 
20 } 
21 
22 // perform output to a binary file 
23 ofstream output ("temp.bin", ios::binary); 
24 copy (vd.begin(), vd.end(), oi_t(output, (char *)NULL)); 
25 output.close(); 
26 
27 // input from the binary file to a container 
28 std::vector<double> vi; 
29 ifstream input ("temp.bin", ios::binary); 
30 ii_t ii(input); 
31 copy (ii, ii_t(), back_inserter(vi)); 
32 input.close(); 
33 
34 // output data to screen to verify/compare the results 
35 for (int i = 0; i < vd.size(); i++) 
36  printf ("%8.4f %8.4f\n", vd[i], vi[i]); 
37 
38 printf ("vd.size() = %d\tvi.size() = %d\n", vd.size(), vi.size()); 
39 return 0; 
40 } 

La sortie résultante est la suivante et a deux problèmes, afaik:

1804.2894 1804.2985 
846.9309 0.9312 
1681.6928 0.6917 
1714.6369 0.6420 
1957.7478 0.7542 
424.2383 0.2387 
719.8854 0.8852 
1649.7605 0.7660 
596.5166 0.5171 
1189.6414 0.6410 
1025.2024 0.2135 
1350.4900 0.4978 
783.3687 0.3691 
1102.5201 0.5220 
2044.8978 0.9197 
1967.5139 0.5114 
1365.1805 0.1815 
1540.3834 0.3830 
304.0892 0.0891 
1303.4557 0.4600 
vd.size() = 20 vi.size() = 20 

1) Chaque double lecture à partir des données binaires ne contient pas les informations avant la virgule. 2) Les données sont mutilées à la 3e décimale (ou plus tôt) et une erreur arbitraire est introduite.

S'il vous plaît toute aide serait appréciée. (J'aimerais que quelqu'un me pointe sur un article précédent à ce sujet, car j'ai trouvé ma recherche)

Répondre

3

Pour la question 1) Vous devez spécifier un séparateur (par exemple un espace). La partie non décimale était collée à la partie décimale du nombre précédent. Lancer et utiliser NULL est généralement faux en C++. Aurait été un soupçon;)

copy (vd.begin(), vd.end(), oi_t(output, " ")); 

Pour la question 2)

#include <iomanip> 
output << setprecision(9); 
+0

juste curieux - pourquoi un séparateur est-il nécessaire dans la définition d'ostream_iterator? Un octet est-il réellement utilisé à chaque fois que oi est utilisé pour désigner un espace vide? Ne pouvons-nous pas faire sans cet octet supplémentaire et juste lire dans le bon nombre d'octets correspondant à un double et renoncer à un séparateur? – Shamster

+0

En fait, votre fichier n'est pas un fichier binaire est la façon dont vous l'attendez. Ouvrez simplement le fichier temp.bin dans un éditeur de texte. Je ne pense pas que vous pouvez utiliser les manipulateurs de flux comme vous le faites pour stocker les données au format binaire. –

+0

oui, je remarquais qu'ils n'étaient pas stockés en format binaire. Quelle est la manière idiomatique de produire des données binaires à partir d'un conteneur STL en tant que tel? – Shamster

1

Vous pouvez définir la précision en utilisant setprecision comme Tristram a souligné, et avez besoin d'un delimiter vous. Voir le cppreference pour voir comment fonctionnent les fonctions operator=. Il n'y a pas de jeu de format, vous aurez donc besoin de le mettre sur la sortie:

ofstream output ("temp.bin", ios::binary); 
output.flags(ios_base::fixed); //or output << fixed; 
copy(vd.begin(), vd.end(), oi_t(output, " ")); 
output.close(); 

je aurais tendance à favoriser l'utilisation fixed pour éliminer les problèmes de précision. Il y a eu de nombreux cas où quelqu'un pensait «nous n'aurons jamais besoin de plus de 5 chiffres», alors ils ont codé en dur une précision partout. Ce sont des bugs coûteux à corriger.

3

Pour écrire des données binaires à l'aide de std :: copy().
Je fais ceci:

template<typename T> 
struct oi_t: public iterator<output_iterator_tag, void, void, void, void> 
{ 
    oi_t(std::ostream& str) 
    :m_str(str) 
    {} 
    oi_t& operator++() {return *this;} // increment does not do anything. 
    oi_t& operator++(int){return *this;} 
    oi_t& operator*() {return *this;} // Dereference returns a reference to this 
             // So that when the assignment is done we 
             // actually write the data from this class 
    oi_t& operator=(T const& data) 
    { 
    // Write the data in a binary format 
    m_str.write(reinterpret_cast<char const*>(&data),sizeof(T)); 
    return *this; 
    } 

    private: 
    std::ostream& m_str; 
}; 

Ainsi l'appel à std :: copie est:

copy (vd.begin(), vd.end(), oi_t<double>(output)); 

L'itérateur d'entrée est un peu plus compliqué que nous devons tester la fin du flux.

template<typename T> 
struct ii_t: public iterator<input_iterator_tag, void, void, void, void> 
{ 
    ii_t(std::istream& str) 
    :m_str(&str) 
    {} 
    ii_t() 
    :m_str(NULL) 
    {} 
    ii_t& operator++() {return *this;} // increment does nothing. 
    ii_t& operator++(int){return *this;} 
    T& operator*() 
    { 
    // On the de-reference we actuall read the data into a local //// static //// 
    // Thus we can return a reference 
    static T result; 
    m_str->read(reinterpret_cast<char*>(&result),sizeof(T)); 
    return result; 
    } 
    // If either iterator has a NULL pointer then it is the end() of stream iterator. 
    // Input iterators are only equal if they have read past the end of stream. 
    bool operator!=(ii_t const& rhs) 
    { 
     bool lhsPastEnd = (m_str == NULL)  || (!m_str->good()); 
     bool rhsPastEnd = (rhs.m_str == NULL) || (!rhs.m_str->good()); 

     return !(lhsPastEnd && rhsPastEnd); 
    } 

    private: 
    std::istream* m_str; 
}; 

L'appel à lire l'entrée est maintenant:

ii_t<double> ii(input); 
copy (ii, ii_t<double>(), back_inserter(vi)); 
+0

'operator *' et 'operator ++' devraient probablement renvoyer une référence plutôt qu'une copie, pas que la copie de l'itérateur est chère –

+0

@ Tux-D 'bool lhsPastEnd ==' ou 'bool lhsPastEnd ='? – suresh

+0

@suresh: Merci. Après 2 ans, je l'ai réparé. –

-1

Je suis venu avec une meilleure conception pour les E/S binaires. L'approche fondamentale est d'avoir trois méthodes: size_on_stream, load_from_buffer, et store_to_buffer. Ceux-ci vont dans une classe d'interface de sorte que toutes les classes qui supportent les E/S binaires l'héritent. La méthode size_on_stream renvoie la taille des données transmises sur le flux. Généralement, cela n'inclut pas les octets de remplissage. Cela devrait être récursif de sorte qu'une classe appelle la méthode sur tous ses membres.

La méthode load_from_buffer est passée une référence à un pointeur vers un tampon (unsigned char * &). La méthode charge les membres de données de l'objet à partir du tampon, en incrémentant le pointeur après chaque membre (ou en l'incrémentant une fois tous les membres).

La méthode store_to_buffer stocke des données dans le tampon donné et incrémente le pointeur.

Le client appelle size_on_stream pour déterminer la taille de toutes les données. Un tampon de cette taille est alloué dynamiquement. Un autre pointeur vers ce tampon est passé au store_to_buffer pour stocker les membres de l'objet dans le tampon. Enfin, le client utilise une écriture binaire (fwrite or std::ostream::write) pour transférer le tampon dans le flux. Certains des avantages de cette technique sont: le compactage, l'abstraction et les E/S de bloc. Les objets emballent leurs membres dans le tampon. Le processus d'écriture dans le tampon est caché du client. Le client peut utiliser des fonctions d'E/S de bloc qui sont toujours plus efficaces que le transfert de membres individuels.

Cette conception est également plus portable, car les objets peuvent prendre soin de l'Endianess. Il y a une méthode simple pour cela, qui est laissée au lecteur. J'ai développé ce concept pour incorporer également les types POD (Plain Old Data), ce qui est laissé comme un exercice pour le lecteur.