2008-11-26 17 views
18

क्या सी में बाइनरी डेटा को पढ़ने और पार्स करने के लिए कोई पुस्तकालय या गाइड हैं?सी में बाइनरी डेटा पार्सिंग?

मैं कुछ कार्यक्षमता देख रहा हूं जो नेटवर्क सॉकेट पर टीसीपी पैकेट प्राप्त करेंगे और फिर उस विनिर्देश के अनुसार उस बाइनरी डेटा को पार्स कर दें, जिससे जानकारी कोड द्वारा अधिक उपयोग योग्य रूप में बदल जाएगी।

क्या वहां कोई पुस्तकालय हैं जो ऐसा करते हैं, या यहां तक ​​कि इस प्रकार की चीज करने पर एक प्राइमर भी हैं?

उत्तर

15

सी/सी में ऐसा करने का मानक तरीका ++ वास्तव में के रूप में 'gwaredd' structs के लिए कास्टिंग है सुझाव दिया

यह रूप में असुरक्षित के रूप में एक लगता होगा नहीं है। आपने पहली बार उस संरचना को डाला जिसे आपने उम्मीद की थी, जैसा कि उसके उदाहरण में, तब आप वैधता के लिए उस संरचना का परीक्षण करते हैं। आपको अधिकतम/न्यूनतम मान, समाप्ति अनुक्रम, आदि के लिए परीक्षण करना होगा

आप जिस प्लेटफॉर्म पर हैं, उसे Unix Network Programming, Volume 1: The Sockets Networking API पढ़ना होगा। इसे खरीदें, इसे उधार लें, चोरी करें (पीड़ित समझ जाएगा, यह भोजन चोरी या कुछ चोरी करने जैसा है ...), लेकिन इसे पढ़ो।

स्टीवंस पढ़ने के बाद, इनमें से अधिकतर बहुत अधिक समझ में आएंगे।

+1

मैं "कास्ट फिर चेक" विधि के बारे में संदेह कर रहा हूं। यदि आप चेक नहीं करते हैं, तो आपको अमान्य डेटा प्राप्त करने का जोखिम होता है। और यदि आप जांचते हैं, कास्टिंग का क्या मतलब है? पारंपरिक पार्सिंग के रूप में जांच धीमी होगी। – bortzmeyer

+1

केसी बार्कर ने नीचे लिखा है, चीजें इतनी सरल नहीं हैं। आप बाइट संरेखण और अधिकांश समय पैडिंग को ठीक कर सकते हैं (और आपको इसके बारे में पता होना चाहिए और प्रत्येक नई प्रणाली के साथ इसे पूरी तरह से जांचना होगा), लेकिन एक बार जब आप एंडियन ऑर्डर समस्याओं में आ जाएंगे, तो आपको जांच से पहले प्रत्येक स्ट्रक्चर को अलग-अलग ठीक करने के लिए मजबूर होना होगा वैधता के लिए। और यदि आप वैधता की जांच कर रहे हैं, तो आप इसे पार्सिंग के दौरान भी देख सकते हैं। व्यक्तिगत टोकन पार्सिंग भी ठीक दागदार subclassing और संस्करणिंग की अनुमति देता है। – Groo

+0

वास्तव में Office 2010 में Office फ़ाइल सत्यापन शुरू किया गया था और बाद में Office 2007 और Office 2003 में बैकपोर्ट किया गया था, मूल रूप से कमजोरियों को शोषण से रोकने के लिए वैधता के लिए फ़ाइल की जांच करता है। –

3

आपको सी में बाइनरी डेटा को पार्स करने की ज़रूरत नहीं है, बस कुछ पॉइंटर डालें जो आपको लगता है कि यह होना चाहिए।

struct SomeDataFormat 
{ 
    .... 
} 

SomeDataFormat* pParsedData = (SomeDataFormat*) pBuffer; 

बस endian मुद्दों, प्रकार आकार से सावधान रहना,

+4

या विभिन्न कंपाइलर, आदि यह * वास्तव में * नाजुक कोड, आईएमओ है। –

+0

सहमत हुए। मुझे लगता है कि कई सारे इत्यादि हैं कि वह पुस्तकालय चाहता है कि वह ऐसा करे। –

+0

हाँ-.- जब तक आप एक ही मशीन पर हैं, तब तक यह दृष्टिकोण उचित है, नेटवर्क प्रोग्रामिंग में ऐसा करना वास्तव में टालना चाहिए। –

5

, बफ़र्स के अंत पढ़ने आदि आदि आप Google Protocol Buffers में रुचि हो सकती है, जो मूल रूप से एक क्रमबद्धता रूपरेखा है। यह मुख्य रूप से सी ++/जावा/पायथन (वे Google द्वारा समर्थित भाषाओं) के लिए हैं, लेकिन C सहित अन्य भाषाओं में इसे बंद करने के लिए सतत प्रयास हैं। (मैंने सी बंदरगाह का उपयोग नहीं किया है, लेकिन मैं सी # बंदरगाहों में से एक के लिए ज़िम्मेदार हूं।)

+1

डेटा को क्रमबद्ध करने के कई तरीके हैं (प्रोटोकॉल बफर अच्छा है लेकिन यह केवल एक है, एक्सएमएल, जेएसओएन, एएसएन/1 + बीईआर, आदि भी है)। वे केवल तभी काम करते हैं जब आप प्रोटोकॉल के विनिर्देश को नियंत्रित करते हैं। यदि यह मामला नहीं है, तो आपकी विधि काम नहीं करती है। – bortzmeyer

+0

बिल्कुल। यदि आप प्रोटोकॉल के नियंत्रण में नहीं हैं, तो आपको मूल रूप से इसे मैन्युअल रूप से करना होगा। –

1

मैं वास्तव में समझ नहीं पा रहा हूं कि आप किस प्रकार की लाइब्रेरी की तलाश में हैं? सामान्य पुस्तकालय जो किसी भी बाइनरी इनपुट लेगा और इसे अज्ञात प्रारूप में पार्स करेगा? मुझे यकीन नहीं है कि ऐसी लाइब्रेरी कभी भी किसी भी भाषा में मौजूद हो सकती है। मुझे लगता है कि आपको अपने प्रश्न को थोड़ा सा विस्तृत करने की आवश्यकता है।

संपादित:
ठीक है, तो Jon's जवाब पढ़ने के बाद लगता है एक पुस्तकालय, अच्छी तरह से पुस्तकालय की तरह इसे और अधिक कोड पीढ़ी उपकरण की तरह है नहीं है। लेकिन जैसा कि कई ने उचित डेटा संरचना में डेटा कास्टिंग किया है, उचित सावधानी के साथ यानी पैक किए गए ढांचे का उपयोग करके और एंडियन मुद्दों की देखभाल करना आप अच्छे हैं। सी के साथ इस तरह के उपकरण का उपयोग करना यह सिर्फ एक ओवरकिल है।

2

पार्सिंग/स्वरूपण बाइनरी संरचना में से कुछ है चीजें जो उच्च स्तर/प्रबंधित भाषाओं की तुलना में सी में करना आसान है। आप बस उस प्रारूप को परिभाषित करते हैं जो उस प्रारूप से मेल खाता है जिसे आप संभालना चाहते हैं और संरचना पार्सर/फॉर्मेटर है। यह काम करता है क्योंकि सी में एक संरचना एक सटीक मेमोरी लेआउट का प्रतिनिधित्व करती है (जो, ज़ाहिर है, पहले से ही बाइनरी है)। Kervin और gwaredd के जवाब भी देखें।

1

मूल रूप से struct पर कास्टिंग के बारे में सुझाव लेकिन कृपया ध्यान रखें कि विभिन्न आर्किटेक्चर पर संख्याओं का अलग-अलग प्रतिनिधित्व किया जा सकता है।

एंडियन मुद्दों से निपटने के लिए नेटवर्क बाइट ऑर्डर पेश किया गया था - सामान्य अभ्यास डेटा भेजने से पहले और बाइट ऑर्डर को मेजबान ऑर्डर से वापस करने के लिए होस्ट बाइट ऑर्डर से संख्याओं को परिवर्तित करना है। कार्य htonl, htons, ntohl और ntohs देखें।

और वास्तव में केर्विन की सलाह पर विचार करें - UNP पढ़ें। आपको पछतावा नहीं होगा!

12

मुझे यह देखने के लिए अपने प्रश्न को दोबारा शुरू करने दें कि मैं ठीक से समझ गया हूं या नहीं। आप ऐसे सॉफ़्टवेयर की तलाश में हैं जो पैकेट का औपचारिक विवरण लेगा और फिर ऐसे पैकेट को पार्स करने के लिए "डिकोडर" का उत्पादन करेगा?

यदि ऐसा है, तो उस क्षेत्र में संदर्भ PADS है। एक अच्छा आलेख इसे पेश करना PADS: A Domain-Specific Language for Processing Ad Hoc Data है। पीएडीएस बहुत पूरा है लेकिन दुर्भाग्यवश एक गैर-मुक्त लाइसेंस के तहत।

संभावित विकल्प हैं (मैंने गैर-सी समाधानों का उल्लेख नहीं किया है)।

आप फ्रेंच पढ़ा है, मैं Génération de décodeurs de formats binaires में इन मुद्दों को संक्षेप: जाहिर है, कोई भी पूरी तरह से उत्पादन के लिए तैयार के रूप में माना जा सकता है।

+0

@bortzmeyer ये सभी मेरे लिए समाचार हैं। जानकारी के लिए धन्यवाद! – Bklyn

10

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

फिर, उदाहरण के लिए आप अपने प्रत्येक प्रोटोकॉल संदेशों के लिए struct एस परिभाषित कर सकते हैं, और प्रत्येक के लिए पैक/अनपैक (कुछ लोग उन्हें धारावाहिक/deserialize कहते हैं) लिखते हैं।

आधार मामले के रूप में, एक एकल 8-बिट पूर्णांक निकालने के लिए एक आदिम इस तरह दिख सकता है (मेजबान मशीन पर 8-बिट char मानते हुए, आप कस्टम प्रकारों की एक परत जोड़ सकते हैं ताकि यह सुनिश्चित किया जा सके कि यदि आवश्यक हो,):

const void * read_uint8(const void *buffer, unsigned char *value) 
{ 
    const unsigned char *vptr = buffer; 
    *value = *buffer++; 
    return buffer; 
} 

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

अब, हम एक 16-बिट अहस्ताक्षरित मात्रा को पढ़ने के लिए एक समान कार्य लिख सकते हैं:

const void * read_uint16(const void *buffer, unsigned short *value) 
{ 
    unsigned char lo, hi; 

    buffer = read_uint8(buffer, &hi); 
    buffer = read_uint8(buffer, &lo); 
    *value = (hi << 8) | lo; 
    return buffer; 
} 

यहाँ मैं आने वाले डेटा ग्रहण बड़े endian है, इस नेटवर्किंग प्रोटोकॉल में आम है (मुख्य रूप से ऐतिहासिक कारणों के लिए) । आप निश्चित रूप से चालाक हो सकते हैं और कुछ पॉइंटर अंकगणित कर सकते हैं और अस्थायी की आवश्यकता को हटा सकते हैं, लेकिन मुझे लगता है कि इस तरह से यह समझने में आसान और आसान हो जाता है। डिबगिंग करते समय इस तरह के आदिम में अधिकतम पारदर्शिता होने पर एक अच्छी बात हो सकती है।

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

+0

आपका मतलब है * vptr ++, क्या आप नहीं, चार में बफर का कलाकार भी गायब है। – mloskot

28

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

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

संरेखण: आप जिस वास्तुकला का उपयोग कर रहे हैं वह एक विषम-संबोधित ऑफसेट पर एक मल्टी-बाइट शब्द लोड करने में सक्षम हो सकता है, लेकिन कई आर्किटेक्चर नहीं कर सकते हैं। यदि एक 4-बाइट शब्द 4-बाइट संरेखण सीमा को झुकाता है, तो लोड कचरा खींच सकता है। यहां तक ​​कि यदि प्रोटोकॉल के पास शब्दों को गलत तरीके से गलत नहीं किया गया है, तो कभी-कभी बाइट स्ट्रीम को गलत तरीके से गलत तरीके से गलत किया जाता है। (उदाहरण के लिए, हालांकि IP हेडर परिभाषा सभी 4 बाइट शब्द 4 बाइट सीमाओं पर डालता है, अक्सर ईथरनेट हैडर 2-बाइट सीमा पर IP हेडर ही धक्का।)

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

बिट ऑर्डरिंग: सी बिटफील्ड के अंदर बिट्स का ऑर्डरिंग कंपाइलर-विशिष्ट है। इसके अलावा, बिट्स को आपके रनटाइम कोड के लिए "एट" करना मुश्किल होता है। प्रत्येक बार जब आप किसी स्ट्रक्चर के अंदर बिटफील्ड का संदर्भ लेते हैं, तो कंपाइलर को मास्क/शिफ्ट ऑपरेशंस का एक सेट उपयोग करना होता है। बेशक, आपको उस बिंदु पर मास्किंग/स्थानांतरण करना होगा, लेकिन यदि गति चिंता का विषय है तो हर संदर्भ में इसे न करें। (यदि अंतरिक्ष ओवरराइडिंग चिंता है, तो बिटफिल्ड का उपयोग करें, लेकिन ध्यान से चलें।)

यह सब नहीं कहता है कि "structs का उपयोग न करें।" मेरा पसंदीदा दृष्टिकोण सभी प्रासंगिक प्रोटोकॉल डेटा के किसी भी बिटफील्ड के बिना और मुद्दों के लिए चिंता के बिना एक दोस्ताना देशी-एंडियन संरचना घोषित करना है, फिर सममित पैक/पार्स रूटीन का एक सेट लिखें जो संरचना को बीच के बीच के रूप में उपयोग करता है।

typedef struct _MyProtocolData 
{ 
    Bool myBitA; // Using a "Bool" type wastes a lot of space, but it's fast. 
    Bool myBitB; 
    Word32 myWord; // You have a list of base types like Word32, right? 
} MyProtocolData; 

Void myProtocolParse(const Byte *pProtocol, MyProtocolData *pData) 
{ 
    // Somewhere, your code has to pick out the bits. Best to just do it one place. 
    pData->myBitA = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_A_MASK >> MY_BIT_A_SHIFT; 
    pData->myBitB = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_B_MASK >> MY_BIT_B_SHIFT; 

    // Endianness and Alignment issues go away when you fetch byte-at-a-time. 
    // Here, I'm assuming the protocol is big-endian. 
    // You could also write a library of "word fetchers" for different sizes and endiannesses. 
    pData->myWord = *(pProtocol + MY_WORD_OFFSET + 0) << 24; 
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 1) << 16; 
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 2) << 8; 
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 3); 

    // You could return something useful, like the end of the protocol or an error code. 
} 

Void myProtocolPack(const MyProtocolData *pData, Byte *pProtocol) 
{ 
    // Exercise for the reader! :) 
} 

अब, अपने कोड के बाकी सिर्फ अनुकूल, तेजी से struct वस्तुओं के अंदर डेटा manipulates और केवल पैक/पार्स आप एक बाइट धारा के साथ इंटरफेस करने के लिए है जब कहता है। एनओटीएच या हटन के लिए कोई ज़रूरत नहीं है, और आपके कोड को धीमा करने के लिए कोई बिटफील्ड नहीं है।

+0

क्या यह कोड सॉकेट के माध्यम से संरचना को पार करने के लिए भी काम करता है >>> – codingfreak

+0

यह सॉकेट के लिए स्पष्ट रूप से अच्छा है - खासकर जब आप सॉकेट के किसी भी छोर पर प्रक्रियाओं की समाप्ति/बस चौड़ाई/संरेखण के बारे में दावा नहीं करना चाहते हैं । –

+0

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

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