2009-05-13 14 views
15

मेरे पास एक बाइनरी फ़ाइल है जो यूनिक्स मशीन पर बनाई गई थी। यह सिर्फ एक के बाद लिखा रिकॉर्ड का एक गुच्छा है। रिकॉर्ड को इस तरह कुछ परिभाषित किया गया है:मैं एक बड़े एंडियन संरचना को थोड़ा एंडियन-स्ट्रक्चर में कैसे परिवर्तित करूं?

struct RECORD { 
    UINT32 foo; 
    UINT32 bar; 
    CHAR fooword[11]; 
    CHAR barword[11]; 
    UNIT16 baz; 
} 

मैं यह पता लगाने की कोशिश कर रहा हूं कि मैं विंडोज़ मशीन पर इस डेटा को कैसे पढ़ और समझूं। मेरे पास ऐसा कुछ है:

fstream f; 
f.open("file.bin", ios::in | ios::binary); 

RECORD r; 

f.read((char*)&detail, sizeof(RECORD)); 

cout << "fooword = " << r.fooword << endl; 

मुझे डेटा का एक गुच्छा मिलता है, लेकिन यह डेटा मुझे उम्मीद नहीं है। मुझे संदेह है कि मेरी समस्या मशीनों के अंतराल के अंतर से है, इसलिए मैं इसके बारे में पूछने आया हूं।

मैं समझता हूं कि एकाधिक बाइट विंडोज़ पर छोटे-एंडियन में संग्रहीत किए जाएंगे और यूनिक्स पर्यावरण में बड़े-एंडियन होंगे, और मुझे वह मिल जाएगा। दो बाइट्स के लिए, विंडोज़ पर 0x1234 यूनिक्स सिस्टम पर 0x3412 होगा।

क्या अंततः संरचना के बाइट ऑर्डर को पूरी तरह से या संरचना के प्रत्येक सदस्य के सदस्य को प्रभावित करता है? एक यूनिक्स सिस्टम पर बनाई गई संरचना को बदलने के लिए मैं क्या दृष्टिकोण ले सकता हूं जिसमें विंडोज सिस्टम पर एक ही डेटा है? कुछ बाइट्स जो कि बाइट्स के बाइट ऑर्डर की तुलना में अधिक गहराई से अधिक हैं, भी बहुत अच्छा होगा!

+1

आपने उनके बारे में नहीं पूछा, लेकिन इस तरह के विरासत कोड के साथ काम करते समय विचार करने के लिए एक और बात बिटफील्ड है। ऑर्डर जो बिटफिल्ड पैक किए गए हैं, वे संकलक और प्लेटफार्म दोनों निर्भर और प्रोसेसर की अंतहीनता से असंबंधित हो सकते हैं। – Dan

उत्तर

12

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

संपादित करें: अगर संरचना को पैकिंग के साथ लिखा गया था, तो यह काफी सरल होना चाहिए। तब

// Functions to swap the endian of 16 and 32 bit values 

inline void SwapEndian(UINT16 &val) 
{ 
    val = (val<<8) | (val>>8); 
} 

inline void SwapEndian(UINT32 &val) 
{ 
    val = (val<<24) | ((val<<8) & 0x00ff0000) | 
      ((val>>8) & 0x0000ff00) | (val>>24); 
} 

, एक बार आप struct लोड किया है, बस प्रत्येक तत्व स्वैप: इस (untested) कोड की तरह कुछ काम करना चाहिए

SwapEndian(r.foo); 
SwapEndian(r.bar); 
SwapEndian(r.baz); 
+0

मेरे पास #pragma पैक (पुश, 1) निर्दिष्ट है। – scottm

+0

@ स्कोटी, यह आपकी मदद नहीं करेगा यदि आपके द्वारा पढ़े जा रहे डेटा में पहले से ही स्लैक बाइट हैं। एफडब्ल्यूआईडब्लू, यह वास्तव में तब तक नहीं होना चाहिए जब तक कि कार्यक्रम का डेवलपर पूरी तरह से structs लिख रहा था, जो कि बस बुरा है। स्ट्रक्चर को फ़ील्ड द्वारा क्षेत्र में हमेशा लिखा जाना चाहिए - इस तरह की परिस्थितियों के लिए। – Duck

+0

@ डक, मेरे पास संरचना की परिभाषा का स्रोत है (लेकिन उन्हें पढ़ने या लिखने के लिए नहीं) और इसमें पैक = 1. – scottm

3

यह प्रत्येक सदस्य को स्वतंत्र रूप से प्रभावित करता है, पूरे struct नहीं। इसके अलावा, यह सरणी जैसी चीजों को प्रभावित नहीं करता है। उदाहरण के लिए, यह केवल int एस में रिवर्स ऑर्डर में संग्रहीत करता है।

पीएस। उस ने कहा, अजीब endianness के साथ एक मशीन हो सकती है। जो मैंने अभी कहा है वह सबसे अधिक इस्तेमाल की जाने वाली मशीनों (x86, एआरएम, पावरपीसी, एसपीएआरसी) पर लागू होता है।

+0

"इसके अलावा, यह सरणी जैसी चीजों को प्रभावित नहीं करता है।": लेकिन यह एरे के सदस्यों को प्रभावित करता है अगर वे संख्यात्मक डेटा प्रकार या आकार वाले वर्ण> 1 बाइट हैं! – mmmmmmmm

+1

@ रिस्टवेन्स: हाँ, बिल्कुल। मेरा मतलब है कि यह किसी सरणी में तत्वों के क्रम को प्रभावित नहीं करता है।प्रत्येक सदस्य को स्पष्ट रूप से एक चर के समान माना जाता है। –

10

असल में, अंतहीनता अंतर्निहित हार्डवेयर की संपत्ति है, ओएस नहीं।

सबसे अच्छा समाधान डेटा लिखते समय मानक में कनवर्ट करना है - Google "नेटवर्क बाइट ऑर्डर" के लिए Google और आपको यह करने के तरीके मिलना चाहिए।

संपादित करें: यहाँ लिंक है: http://www.gnu.org/software/hello/manual/libc/Byte-Order.html

+1

मुझे यह तय नहीं करना है कि डेटा कैसे लिखना है, यह प्रक्रिया 10 वर्षों तक हो रही है, और यह बदल नहीं रहा है। – scottm

+1

किस मामले में आपको उपयोग की जाने वाली सटीक तंत्र की खोज करने की आवश्यकता है, और परिवर्तित करने के लिए अपनी खुद की दिनचर्या लिखें (या उन्हें ऑनलाइन खोजें)। नोट, हालांकि, लेखक "बदल नहीं रहा है," यह बेहतर नहीं है कि यह कभी भी किसी अन्य वास्तुकला में न जाए या यह बदलेगा या नहीं। – kdgregory

1

आप एक से अधिक बाइट के प्रत्येक सदस्य की endianess दूर करने के लिए, अलग-अलग है। तारों को परिवर्तित करने की आवश्यकता नहीं है (fooword और barword), क्योंकि उन्हें बाइट्स के अनुक्रम के रूप में देखा जा सकता है।

हालांकि, आपको एक और समस्या का ख्याल रखना चाहिए: आपकी संरचना में सदस्यों के सहयोगी। असल में, आपको यह जांचना होगा कि आकार (रिकॉर्ड्स) यूनिक्स और विंडोज कोड दोनों पर समान है या नहीं। कंपाइलर्स आम तौर पर आपके इच्छित पात्रता को परिभाषित करने के लिए प्रागमा प्रदान करते हैं (उदाहरण के लिए, #pragma पैक)।

1

तुम भी बीच संरेखण अंतर पर विचार करने के लिए है दो कंपाइलरप्रत्येक कंपाइलर को संरचना में सदस्यों के बीच पैडिंग डालने की अनुमति दी जाती है जो आर्किटेक्चर के लिए उपयुक्त है। तो तुम सच में पता करने की जरूरत:

  • कैसे यूनिक्स prog फ़ाइल
  • को लिखते हैं यदि यह वस्तु की एक द्विआधारी प्रतिलिपि संरचना का सही लेआउट है।
  • यदि यह एक बाइनरी प्रति है तो स्रोत आर्किटेक्चर की एंडियन-नेस क्या है।

यही कारण है कि अधिकांश कार्यक्रम (जो मैंने देखा है (जिसे प्लेटफ़ॉर्म तटस्थ होने की आवश्यकता है)) डेटा को टेक्स्ट स्ट्रीम के रूप में क्रमबद्ध करें जिसे मानक iostreams द्वारा आसानी से पढ़ा जा सकता है।

0

इस तरह काम करना चाहिए:

#include <algorithm> 

struct RECORD { 
    UINT32 foo; 
    UINT32 bar; 
    CHAR fooword[11]; 
    CHAR barword[11]; 
    UINT16 baz; 
} 

void ReverseBytes(void *start, int size) 
{ 
    char *beg = start; 
    char *end = beg + size; 

    std::reverse(beg, end); 
} 

int main() { 
    fstream f; 
    f.open("file.bin", ios::in | ios::binary); 

    // for each entry { 
    RECORD r; 
    f.read((char *)&r, sizeof(RECORD)); 
    ReverseBytes(r.foo, sizeof(UINT32)); 
    ReverseBytes(r.bar, sizeof(UINT32)); 
    ReverseBytes(r.baz, sizeof(UINT16) 
    // } 

    return 0; 
} 
1

मैं प्रत्येक डेटा प्रकार है कि अदला-बदली की जरूरत है इस तरह, के लिए एक SwapBytes विधि लागू करना चाहते:

inline u_int ByteSwap(u_int in) 
{ 
    u_int out; 
    char *indata = (char *)&in; 
    char *outdata = (char *)&out; 
    outdata[0] = indata[3] ; 
    outdata[3] = indata[0] ; 

    outdata[1] = indata[2] ; 
    outdata[2] = indata[1] ; 
    return out; 
} 

inline u_short ByteSwap(u_short in) 
{ 
    u_short out; 
    char *indata = (char *)&in; 
    char *outdata = (char *)&out; 
    outdata[0] = indata[1] ; 
    outdata[1] = indata[0] ; 
    return out; 
} 

तो मैं संरचना में फ़ंक्शन जोड़ने इसके लिए स्वैपिंग की आवश्यकता है, इस तरह:

struct RECORD { 
    UINT32 foo; 
    UINT32 bar; 
    CHAR fooword[11]; 
    CHAR barword[11]; 
    UNIT16 baz; 
    void SwapBytes() 
    { 
    foo = ByteSwap(foo); 
    bar = ByteSwap(bar); 
    baz = ByteSwap(baz); 
    } 
} 

फिर आप अपने कोड को संशोधित कर सकते हैं जो पढ़ता है (या लिखता है) वह इस तरह की संरचना:

fstream f; 
f.open("file.bin", ios::in | ios::binary); 

RECORD r; 

f.read((char*)&detail, sizeof(RECORD)); 
r.SwapBytes(); 

cout << "fooword = " << r.fooword << endl; 

विभिन्न प्लेटफार्मों तुम सिर्फ प्रत्येक ByteSwap अधिभार के एक मंच विशिष्ट कार्यान्वयन की आवश्यकता का समर्थन करने के।

4

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

एक अच्छा वैकल्पिक तरीका हेडर को पढ़ने के लिए है, जो भी, एक बफर में और तीन से पार्स में है, परमाणु संचालन में I/O ओवरहेड से बचने के लिए एक हस्ताक्षरित 32 बिट पूर्णांक पढ़ने के लिए!

char buffer[32]; 
char* temp = buffer; 

f.read(buffer, 32); 

RECORD rec; 
rec.foo = parse_uint32(temp); temp += 4; 
rec.bar = parse_uint32(temp); temp += 4; 
memcpy(&rec.fooword, temp, 11); temp += 11; 
memcpy(%red.barword, temp, 11); temp += 11; 
rec.baz = parse_uint16(temp); temp += 2; 

parse_uint32 की घोषणा इस प्रकार दिखाई देगा:

uint32 parse_uint32(char* buffer) 
{ 
    uint32 x; 
    // ... 
    return x; 
} 

यह एक बहुत ही सरल अमूर्त है, यह सूचक भी अपडेट करने के व्यवहार में कोई अतिरिक्त खर्च नहीं करता है:

uint32 parse_uint32(char*& buffer) 
{ 
    uint32 x; 
    // ... 
    buffer += 4; 
    return x; 
} 

बाद का फॉर्म बफर को पार्स करने के लिए क्लीनर कोड की अनुमति देता है; जब आप इनपुट से विश्लेषण करते हैं तो पॉइंटर स्वचालित रूप से अपडेट हो जाता है।

इसी तरह, memcpy एक सहायक हो सकता है, कुछ की तरह:

void parse_copy(void* dest, char*& buffer, size_t size) 
{ 
    memcpy(dest, buffer, size); 
    buffer += size; 
} 

व्यवस्था के इस प्रकार की सुंदरता आप नाम स्थान "little_endian" और "big_endian", तो आप इस में क्या कर सकते हैं हो सकता है वह यह है कि अपने कोड:

using little_endian; 
// do your parsing for little_endian input stream here.. 

आसान एक ही कोड के लिए endianess स्विच करने के लिए, हालांकि, शायद ही कभी जरूरत सुविधा .. फ़ाइल स्वरूपों आमतौर पर वैसे भी एक निश्चित endianess है।

वर्चुअल विधियों के साथ कक्षा में इसे अमूर्त न करें; सिर्फ भूमि के ऊपर जोड़ने के लिए, लेकिन करने के लिए स्वतंत्र यदि ऐसा है तो इच्छुक लगेगा:

little_endian_reader reader(data, size); 
uint32 x = reader.read_uint32(); 
uint32 y = reader.read_uint32(); 

पाठक वस्तु स्पष्ट रूप से सिर्फ सूचक चारों ओर एक पतली आवरण होगा। आकार पैरामीटर त्रुटि जांच के लिए होगा, यदि कोई हो। इंटरफ़ेस प्रति-सी के लिए वास्तव में अनिवार्य नहीं है।

ध्यान दें कि अंतहीनता का चयन COMPILATION TIME पर किया गया था (चूंकि हम little_endian_reader ऑब्जेक्ट बनाते हैं), इसलिए हम वर्चुअल विधि ओवरहेड को विशेष रूप से अच्छे कारण के लिए नहीं बुलाते हैं, इसलिए मैं इस दृष्टिकोण के साथ नहीं जाऊंगा। ;-)

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

मेरा मतलब है, क्या यह जटिल दिखता है?

uint32 xsize = buffer.read<uint32>(); 
uint32 ysize = buffer.read<uint32>(); 
float aspect = buffer.read<float>();  

कोड यह अच्छा लग सकता है, और वास्तव में कम ओवरहेड हो सकता है! यदि endianess फ़ाइल और वास्तुकला कोड के लिए संकलित किया गया है के लिए एक ही है, innerloop इस तरह दिख सकता:

uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4; 
return value; 

कि कुछ आर्किटेक्चर पर अवैध हो सकता है, ताकि अनुकूलन एक बुरा विचार हो सकता है, और धीमी उपयोग करते हैं, लेकिन और अधिक मजबूत दृष्टिकोण:

uint32 value = ptr[0] | (static_cast<uint32>(ptr[1]) << 8) | ...; ptr += 4; 
return value; 

एक x86 कि bswap या mov में संकलित कर सकते हैं, जो काफी कम भूमि के ऊपर है, तो विधि inlined है पर; कंपाइलर इंटरमीडिएट कोड में "चाल" नोड डालेगा, और कुछ भी नहीं, जो काफी कुशल है। अगर संरेखण एक समस्या है तो पूर्ण पठन-शिफ्ट-या अनुक्रम उत्पन्न हो सकता है, बाहर निकल सकता है, लेकिन अभी भी बहुत कमजोर नहीं है। यदि एलएसबी के पते का परीक्षण करें और देखें कि पार्सिंग के तेज या धीमी संस्करण का उपयोग कर सकते हैं तो तुलना-शाखा ऑप्टिमाइज़ेशन की अनुमति दे सकती है। लेकिन इसका मतलब हर पठन में परीक्षण के लिए जुर्माना होगा। प्रयास के लायक नहीं हो सकता है।

ओह, ठीक है, हम हेडर और सामान पढ़ रहे हैं, मुझे नहीं लगता कि यह बहुत से अनुप्रयोगों में एक बाधा है। अगर कुछ कोडेक कुछ सचमुच तंग आंतरिकता कर रहा है, तो फिर से, एक अस्थायी बफर में पढ़ना और वहां से डिकोडिंग अच्छी तरह से सलाह दी जाती है। वही सिद्धांत .. डेटा की एक बड़ी मात्रा को संसाधित करते समय कोई भी फाइल से बाइट-एट-टाइम नहीं पढ़ता है। खैर, असल में, मैंने उस तरह का कोड बहुत बार देखा और "आप ऐसा क्यों करते हैं" के सामान्य जवाब यह है कि फ़ाइल सिस्टम ब्लॉक पढ़ता है और बाइट्स स्मृति से आते हैं, सच है, लेकिन वे एक गहरी कॉल स्टैक से गुज़रते हैं जो कुछ बाइट प्राप्त करने के लिए उच्च ओवरहेड है!

फिर भी, एक बार पार्सर कोड लिखें और ज़िलियन बार -> महाकाव्य जीत का उपयोग करें।

सीधे फ़ाइल से संरचना में पढ़ना: इसे फोल्ड न करें!

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

  • कोई संबंधित समस्या नहीं^_^