2012-12-30 5 views
12

मैट पीटर्ससन के लिए वेक्टर की प्रतिलिपि बनाने के स्पष्टीकरण के लिए धन्यवाद, यह काम लगता है। यहां कोड स्निपसेट:सी ++ में बाइनरी फ़ाइल में वेक्टर को सही तरीके से कैसे लिखना है?

#include <iostream> 
#include <string.h> 
#include <vector> 
#include <fstream> 

using namespace std; 

class Student 
    { 
    private: 
    char m_name[30]; 
    int m_score; 

    public: 
    Student() 
     { 

     } 
    Student(const Student& copy) 
     { 
      m_score = copy.m_score; //wonder why i can use this statment as 
      strncpy(m_name, copy.m_name, 30); //declare it private 
     } 
     Student(const char name[], const int &score) 
     :m_score(score) 
     { 
      strncpy(m_name, name, 30); 
     } 
     void print() const 
     { 
      cout.setf(ios::left); 
      cout.width(20); 
      cout << m_name << " " << m_score << endl; 
     } 
     }; 


     int main() 
     { 
     vector<Student> student; 
     student.push_back(Student("Alex",19)); 
     student.push_back(Student("Maria",20)); 
     student.push_back(Student("muhamed",20)); 
     student.push_back(Student("Jeniffer",20)); 
     student.push_back(Student("Alex",20)); 
     student.push_back(Student("Maria",21)); 
     { 
     Student temp[student.size()]; 
     unsigned int counter; 
     for(counter = 0; counter < student.size(); ++counter) 
     { 
     temp[counter] = student[counter]; 
     } 

     ofstream fout("data.dat", ios::out | ios::binary); 
     fout.write((char*) &temp, sizeof(temp)); 
     fout.close(); 
     } 

     vector<Student> student2; 
     ifstream fin("data.dat", ios::in | ios::binary); 

     { 
     fin.seekg(0, ifstream::end); 
     int size = fin.tellg()/sizeof (Student); 
     Student temp2[size]; 
     fin.seekg(0, ifstream::beg); 
     fin.read((char*)&temp2, sizeof(temp2)); 
     int counter; 
     for(counter = 0; counter <6; ++counter) 
     { 
     student2.push_back(temp2[counter]); 
     } 
     fin.close(); 
     } 
     vector<Student>::iterator itr = student2.begin(); 
     while(itr != student2.end()) 
     { 
     itr->print(); 
     ++itr; 
     } 
     return 0; 
     } 

लेकिन मुझे अतिथि यह तरीका विशाल स्मृति और बोझिल बर्बाद कर देगा। शायद मैं इसे श्रीमान ओसेलॉट और अन्य सुझाव के साथ लिखने पर विचार करूंगा। उत्तर के लिए सभी को धन्यवाद।

+3

मानक संकेत: 'g ++ -Wall -g' के साथ संकलित करें, कोई चेतावनी नहीं दिए जाने तक अपना कोड सुधारें, इसे डीबग करने के लिए' gdb' और 'valgrind' का उपयोग करना सीखें। –

+0

अच्छी तरह से मैं टर्मिनल संकलित तकनीक के साथ अभी तक उपयोग नहीं किया जा रहा है। मुझे लगता है मुझे इसे किसी भी तरह सीखना चाहिए। उत्तर के लिए धन्यवाद। – dchochan

+0

फिर, 'मेक' का उपयोग करना सीखें और अपना सरल 'मेकफ़ाइल' –

उत्तर

8

आप वेक्टर संरचना दर्ज करने के लिए लिख रहे हैं, न कि इसके डेटा बफर। करने के लिए प्रक्रिया लेखन को बदलने का प्रयास करें:

ofstream fout("data.dat", ios::out | ios::binary); 
fout.write((char*)&student[0], student.size() * sizeof(Student)); 
fout.close(); 

और फ़ाइल आकार से वेक्टर की गणना आकार के बजाय, यह बेहतर लिखने वेक्टर आकार (वस्तुओं की संख्या) से पहले है। इस मामले में आप एक ही फाइल को अन्य डेटा पर लिख सकते हैं।

size_t size = student.size(); 
fout.write((char*)&size, sizeof(size)); 
+0

क्या आप अंतिम पंक्ति पर 'आकार (आकार)' रखना चाहते थे? –

3

आप शायद std::vector पर बाइनरी (जिस तरह से आप कर रहे हैं) में नहीं लिख सकते हैं क्योंकि उस टेम्पलेट में आंतरिक पॉइंटर्स हैं, और उन्हें लिखना और फिर से पढ़ना अर्थहीन है।

कुछ सामान्य सलाह:

  • किसी भी एसटीएल टेम्पलेट कंटेनर (जैसे std::vector या std::map), वे निश्चित रूप से आंतरिक संकेत है कि तुम सच है के रूप में लिखने के लिए नहीं करना चाहती शामिल बाइनरी में नहीं लिखते। यदि आपको वास्तव में उन्हें लिखना है, तो अपने लेखन और दिनचर्या को पढ़ना (उदाहरण के लिए एसटीएल इटरेटर्स का उपयोग करना)।

  • देखभाल के बिना strcpy का उपयोग करने से बचें। यदि नाम 30 से अधिक वर्ण हैं तो आपका कोड क्रैश हो जाएगा। कम से कम, strncpy(m_name, name, sizeof(m_name)); का उपयोग करें (लेकिन यह भी 30 वर्णों के नाम के लिए बुरी तरह काम करेगा)। असल में, m_namestd::string होना चाहिए।

  • स्पष्ट रूप से आपके कंटेनर वर्गों को क्रमबद्ध करें (प्रत्येक सार्थक सदस्य डेटा को संभालने के द्वारा)। आप serialize करने के लिए JSON नोटेशन (या शायद YAML, या यहां तक ​​कि एक्सएमएल-जो मुझे बहुत जटिल लगता है, इसलिए अनुशंसा नहीं करते) का उपयोग करने पर विचार कर सकते हैं। यह आपको एक टेक्स्ट डंप प्रारूप देता है, जिसे आप आसानी से मानक संपादक (उदाहरण के लिए emacs या gedit) का निरीक्षण कर सकते हैं। आपको मुफ्त पुस्तकालयों का बहुत सी धारावाहिक मिल जाएगा, उदा। jsoncpp और कई अन्य।

  • g++ -Wall -g के साथ संकलित करना सीखें और gdb डीबगर और valgrind स्मृति रिसाव डिटेक्टर का उपयोग करने के लिए; make का उपयोग करना और Makefile -s लिखना भी सीखें।

  • लाभ लेते हैं कि लिनक्स मुफ्त सॉफ्टवेयर है, इसलिए आप इसके स्रोत कोड को देख सकते हैं (और यदि आप एसटीएल हेडर जटिल हैं तो भी आप stdC++ कार्यान्वयन का अध्ययन करना चाह सकते हैं)।

+0

ठीक उत्तर के लिए धन्यवाद, लेकिन क्या मैं आपसे एक बार फिर पूछ सकता हूं। मैंने विंडोज़ में इस कोड का परीक्षण कुछ बार किया था और कभी-कभी यह सिर्फ क्रश होता था। – dchochan

+0

! तो क्या? आपके प्रोग्राम में ** अपरिभाषित व्यवहार ** है। –

+0

मुझे लगता है कि आप सही हैं, शायद हवा * ws स्मृति प्रबंधन और सुरक्षा linux के रूप में कस नहीं है। – dchochan

2

कार्यों के लिए पढ़ने() और लिखने() के लिए, आपको "सादा पुराना डेटा" या "पीओडी" कहा जाता है। इसका मतलब मूल रूप से है कि कक्षा या संरचना में उनके अंदर कोई पॉइंटर्स नहीं होना चाहिए, और कोई वर्चुअल फ़ंक्शन नहीं होना चाहिए। वेक्टर के कार्यान्वयन में निश्चित रूप से पॉइंटर्स हैं - मुझे आभासी कार्यों के बारे में निश्चित नहीं है।

आपको एक ऐसा फ़ंक्शन लिखना होगा जो एक समय में एक छात्र को स्टोर करता है (या जो बाइट्स के किसी वेक्टर [वेक्टर नहीं] के लिए एक समूह का अनुवाद करता है - लेकिन यह अधिक जटिल है)।

कारण आप गैर-पीओडी डेटा, विशेष पॉइंटर्स में, बाइनरी फ़ाइल में नहीं लिख सकते हैं, यह है कि जब आप डेटा को दोबारा पढ़ते हैं, तो आप लगभग निश्चित रूप से शर्त लगा सकते हैं कि जब आप इसे लिखते हैं तो मेमोरी लेआउट बदल जाता है । यह दुकानों पर एक ही पार्किंग स्थान में पार्क करने की कोशिश करने जैसा थोड़ा सा हो जाता है - अगली बार जब आप पहली बार चालू होते हैं तो प्रवेश द्वार से तीसरे स्थान पर खड़ा होता है, इसलिए आपको एक और स्थान चुनना होगा। कंपाइलर द्वारा पार्किंग रिक्त स्थान के रूप में आवंटित स्मृति और कारों के रूप में छात्र की जानकारी के बारे में सोचें।

[तकनीकी रूप से, इस मामले में, यह और भी बदतर है - आपके वेक्टर में वास्तव में कक्षा के अंदर के छात्रों को शामिल नहीं किया जाता है, जो आप फ़ाइल में लिख रहे हैं, इसलिए आपने छात्रों के बारे में जानकारी भी सहेजी नहीं है , केवल वे कहां स्थित हैं (पार्किंग रिक्त स्थान की संख्या) के बारे में जानकारी]

14

एक फ़ाइल में PODs के vector<T> की दुकान के लिए, आपको वेक्टर, नहीं वेक्टर खुद की सामग्री लिखने के लिए की है। आप &vector[0] के साथ कच्चे डेटा तक पहुंच सकते हैं, पहले तत्व का पता (इसमें कम से कम एक तत्व शामिल है)। कच्चे डेटा लंबाई पाने के लिए, एक तत्व के आकार के साथ वेक्टर में तत्वों की संख्या गुणा करते हैं:

strm.write(reinterpret_cast<const char*>(&vec[0]), vec.size()*sizeof(T)); 

एक ही लागू होता है जब आप फ़ाइल से वेक्टर पढ़ा; तत्व गिनती कुल फ़ाइल आकार एक तत्व के आकार से विभाजित है (यह देखते हुए कि आप केवल फ़ाइल में पॉड का एक प्रकार की दुकान):

const size_t count = filesize/sizeof(T); 
std::vector<T> vec(count); 
strm.read(reinterpret_cast<char*>(&vec[0]), count*sizeof(T)); 

यह केवल काम करता है अगर आप आधारित तत्वों की संख्या की गणना कर सकते फ़ाइल आकार पर (यदि आप केवल एक प्रकार का पीओडी स्टोर करते हैं या यदि सभी वैक्टरों में तत्वों की संख्या समान होती है)। यदि आपके पास अलग-अलग लंबाई वाले विभिन्न पीओडी वाले वैक्टर हैं, तो आपको कच्चे डेटा को लिखने से पहले फ़ाइल में वेक्टर में तत्वों की संख्या लिखनी होगी।

इसके अलावा, जब आप विभिन्न प्रणालियों के बीच बाइनरी रूप में संख्यात्मक प्रकारों को स्थानांतरित करते हैं, तो endianness से अवगत रहें।

संबंधित मुद्दे