2009-06-15 12 views
6

मेरे पास एक हेडर फ़ाइल है जिसमें एक बड़ी संरचना है। मुझे कुछ कार्यक्रमों का उपयोग करके इस संरचना को पढ़ने की जरूरत है और संरचना के प्रत्येक सदस्य पर कुछ संचालन करना है और उन्हें वापस लिखना है।मैं पर्ल के साथ सी हेडर फ़ाइल को कैसे पार्स कर सकता हूं?

उदाहरण के लिए मैं अब

const BYTE Some_Idx[] = { 
4,7,10,15,17,19,24,29, 
31,32,35,45,49,51,52,54, 
55,58,60,64,65,66,67,69, 
70,72,76,77,81,82,83,85, 
88,93,94,95,97,99,102,103, 
105,106,113,115,122,124,125,126, 
129,131,137,139,140,149,151,152, 
153,155,158,159,160,163,165,169, 
174,175,181,182,183,189,190,193, 
197,201,204,206,208,210,211,212, 
213,214,215,217,218,219,220,223, 
225,228,230,234,236,237,240,241, 
242,247,249}; 

जैसे कुछ संरचना है, मैं इस पढ़ने के लिए और सदस्य चर में से प्रत्येक पर कुछ आपरेशन लागू करते हैं और उनका क्रम भिन्न तरह कुछ एक नई संरचना बनाने, की जरूरत है:

const BYTE Some_Idx_Mod_mul_2[] = { 
8,14,20, ... 
... 
484,494,498}; 

क्या कोई पर्ल लाइब्रेरी पहले से ही उपलब्ध है? यदि पर्ल नहीं है, तो पाइथन जैसी कुछ और भी ठीक है।

क्या कोई मदद कर सकता है !!!

+1

सी का उपयोग करने के लिए कोई विशेष कारण नहीं है? मुझे लगता है कि इस समस्या को हल करने का एक तार्किक तरीका है। –

+0

क्या आप सिर्फ सी हेडर पढ़ रहे हैं और संशोधित कर रहे हैं? क्यूं कर? –

+0

@ रयान, क्या आप थोड़ा और विस्तृत हो सकते हैं। क्या आप कह रहे हैं कि मैं उसी सीडर फ़ाइल का उपयोग सी के अंदर से कर सकता हूं, और उसके बाद इसे पार्सिंग के लिए उपयोग कर सकता हूं? @ करसन, दरअसल, मैंने अभी एक उदाहरण दिया है, मुझे विभाजन, विलय और ऐसी चीजों जैसी चीजों को करने की ज़रूरत है। – Alphaneo

उत्तर

9

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

  • एक त्वरित एक बंद स्क्रिप्ट में केवल पार्स करने के लिए (वास्तव में के करीब:

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

  • एक सामान्य, अच्छी तरह लिखित लिपि जो मनमाने ढंग से सी का विश्लेषण कर सकती है और आम तौर पर विभिन्न शीर्षकों पर काम करती है।

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

#!/usr/bin/perl -w 

use strict; 

open FILE, "<header.h" or die $!; 
my @file = <FILE>; 
close FILE or die $!; 

my $in_block = 0; 
my $regex = 'Some_Idx\[\]'; 
my $byte_line = ''; 
my @byte_entries; 
foreach my $line (@file) { 
    chomp $line; 

    if ($line =~ /$regex.*\{(.*)/) { 
     $in_block = 1; 
     my @digits = @{ match_digits($1) }; 
     push @digits, @byte_entries; 
     next; 
    } 

    if ($in_block) { 
     my @digits = @{ match_digits($line) }; 
     push @byte_entries, @digits; 
    } 

    if ($line =~ /\}/) { 
     $in_block = 0; 
    } 
} 

print "const BYTE Some_Idx_Mod_mul_2[] = {\n"; 
print join ",", map { $_ * 2 } @byte_entries; 
print "};\n"; 

sub match_digits { 
    my $text = shift; 
    my @digits; 
    while ($text =~ /(\d+),*/g) { 
     push @digits, $1; 
    } 

    return \@digits; 
} 

मनमाने ढंग से सी पार्स एक छोटे से मुश्किल और कई अनुप्रयोगों के लिए यह नहीं के बराबर होती है, लेकिन शायद आप वास्तव में ऐसा करने की जरूरत है। एक चाल है कि जीसीसी आपके लिए पार्सिंग करे और GCC::TranslationUnit नामक एक सीपीएएन मॉड्यूल का उपयोग करके जीसीसी के पार्स पेड़ में पढ़ें। यहाँ, कोड संकलन करने जीसीसी आदेश है यह सोचते हैं आप test.c नामक एक एकल फ़ाइल है:

जीसीसी -fdump-अनुवाद इकाई -c test.c

यहाँ पार्स में पढ़ने के लिए पर्ल कोड है पेड़:

use GCC::TranslationUnit; 

    # echo '#include <stdio.h>' > stdio.c 
    # gcc -fdump-translation-unit -c stdio.c 
    $node = GCC::TranslationUnit::Parser->parsefile('stdio.c.tu')->root; 

    # list every function/variable name 
    while($node) { 
    if($node->isa('GCC::Node::function_decl') or 
     $node->isa('GCC::Node::var_decl')) { 
     printf "%s declared in %s\n", 
     $node->name->identifier, $node->source; 
    } 
    } continue { 
    $node = $node->chain; 
    } 
3

यदि आपको स्ट्रक्चर को संशोधित करना है, तो आप सीधे विभाजन के लिए रेगेक्स का उपयोग कर सकते हैं और संरचना में प्रत्येक मान में परिवर्तन लागू कर सकते हैं, घोषणा और समाप्ति की तलाश कर सकते हैं}; कब रुकना है पता करने के लिए।

तुम सच में एक अधिक सामान्य समाधान आप एक पार्सर जेनरेटर इस्तेमाल कर सकते हैं, PyParsing

4

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

$ perl -pi.bak -we'if (/const BYTE Some_Idx/ .. /;/) { s/Some_Idx/Some_Idx_Mod_mul_2/g; s/(\d+)/$1 * 2/ge; }' header.h 

कि विश्लेषण करना, -p इनपुट फ़ाइलों के माध्यम से पाश कहते हैं, $_ में प्रत्येक पंक्ति डाल, आपूर्ति कोड चल रहा है, तो मुद्रण $_। -i.bak इन-प्लेस संपादन को सक्षम बनाता है, प्रत्येक मूल फ़ाइल को .bak प्रत्यय के साथ नामित करता है और मूल रूप से नामित एक नई फ़ाइल को प्रिंट करता है। -w चेतावनियों को सक्षम बनाता है। -e '....' प्रत्येक इनपुट लाइन के लिए कोड चलाने के लिए आपूर्ति करता है। header.h एकमात्र इनपुट फ़ाइल है।

पर्ल कोड में, if (/const BYTE Some_Idx/ .. /;/) जांचता है कि हम /const BYTE Some_Idx/ से मेल खाने वाली लाइन के साथ लाइनों की एक श्रृंखला में हैं और /;/ से मेल खाने वाली लाइन के साथ समाप्त हो रहे हैं। एस /.../.../ जी जितना संभव हो उतना प्रतिस्थापन करता है। /(\d+)/ अंकों की एक श्रृंखला से मेल खाता है।/ई ध्वज का परिणाम है ($1 * 2) कोड है जिसे प्रतिस्थापन स्ट्रिंग के बजाय प्रतिस्थापन स्ट्रिंग का उत्पादन करने के लिए मूल्यांकन किया जाना चाहिए। $ 1 वह अंक है जिन्हें प्रतिस्थापित किया जाना चाहिए।

2

वहाँ एक पर्ल मॉड्यूल Parse::RecDescent कहा जाता है जो एक बहुत शक्तिशाली पुनरावर्ती वंश पार्सर जेनरेटर है। यह उदाहरणों के समूह के साथ आता है। उनमें से एक grammar that can parse C है।

अब, मैं नहीं इस मामले को अपने मामले में Parse::Yapp या Parse::EYapp जैसे उपकरणों की तुलना में लगता है, लेकिन पुनरावर्ती वंश पार्स :: RecDescent का उपयोग कर पारसर्स एल्गोरिदम रूप से धीमी (O (n^2), मुझे लगता है) कर रहे हैं। मैंने जांच नहीं की है कि पार्स :: ईवाईएपी इस तरह के सी-पार्सर उदाहरण के साथ आता है, लेकिन यदि ऐसा है, तो वह टूल है जिसे मैं सीखने की सिफारिश करता हूं।

2

पायथन समाधान (पूर्ण नहीं, सिर्फ एक संकेत;)) क्षमा करें यदि कोई गलती - नहीं परीक्षण किया

import re 
text = open('your file.c').read() 
patt = r'(?is)(.*?{)(.*?)(}\s*;)' 
m = re.search(patt, text) 
g1, g2, g3 = m.group(1), m.group(2), m.group(3) 
g2 = [int(i) * 2 for i in g2.split(',') 
out = open('your file 2.c', 'w') 
out.write(g1, ','.join(g2), g3) 
out.close() 
6

क्षमा करें अगर यह एक बेवकूफ सवाल है, लेकिन क्यों सब पर फ़ाइल को पार्स के बारे में चिंता? एक सी प्रोग्राम क्यों न लिखें जिसमें # हेडर शामिल है, इसे आवश्यकतानुसार संसाधित करता है और फिर संशोधित शीर्षलेख के लिए स्रोत को थकाता है। मुझे यकीन है कि यह पर्ल/पायथन समाधान से आसान होगा, और यह अधिक विश्वसनीय होगा क्योंकि हेडर को सी कंपाइलर्स पार्सर द्वारा पार्स किया जा रहा है।

+0

@Neil, हेडर फ़ाइल डेटा अंततः हार्डवेयर के लिए रोम डेटा में बनाया जाएगा। इसे कुछ विशिष्ट प्रारूप की आवश्यकता है, जो रोम टेबल पीढ़ी के अनुरूप है। और विभिन्न चर नामों वाली कई शीर्षलेख फ़ाइलें हैं, और मुझे यकीन नहीं है कि वे रन-टाइम में शामिल हो सकते हैं या नहीं। – Alphaneo

+0

मेरा मतलब यह नहीं है कि आपके असली प्रोग्राम में हेडर शामिल है, मेरा मतलब हेडर को संसाधित करने के लिए किसी अन्य प्रोग्राम को लिखना है, लेकिन उस प्रोग्राम को सी पर्ल या पायथन में लिखें। –

+0

@Neil, मुझे लगता है कि यदि आपके पास कुछ एच (ईडर) फ़ाइलें हैं तो आपका विचार अच्छा होगा। लेकिन हमारे पास यहां लगभग 100+ हेडर फाइलें हैं, जिनमें से प्रत्येक लगभग 10 अलग-अलग डेटास्ट्रक्चर परिभाषा के औसत हैं। हमने सोचा, इनमें से सभी को सी फाइल में शामिल करना एक बड़ा काम होगा। पार्सिंग फॉर्मूला जिसे हम लागू करने की योजना बना रहे हैं, सभी फाइलों के लिए समान है, इसलिए हमने सोचा कि पार्सिंग (मई) एक अच्छा विचार हो सकता है। लेकिन फिर, यदि कोई अन्य विधि काम नहीं करती है तो हम इसे सी का उपयोग करके लागू करेंगे। – Alphaneo

2

Convert::Binary::C नामक एक वास्तव में उपयोगी पर्ल मॉड्यूल है जो सी हेडर फाइलों को पार करता है और पर्ल डेटा संरचनाओं से/से structs को परिवर्तित करता है।

0

डेटा पढ़ने और लिखने के लिए आप हमेशा pack/unpack का उपयोग कर सकते हैं।

#! /usr/bin/env perl 
use strict; 
use warnings; 
use autodie; 

my @data; 
{ 
    open(my $file, '<', 'Some_Idx.bin'); 

    local $/ = \1; # read one byte at a time 

    while(my $byte = <$file>){ 
    push @data, unpack('C',$byte); 
    } 
    close($file); 
} 

print join(',', @data), "\n"; 

{ 
    open(my $file, '>', 'Some_Idx_Mod_mul_2.bin'); 

    # You have two options 
    for my $byte(@data){ 
    print $file pack 'C', $byte * 2; 
    } 
    # or 
    print $file pack 'C*', map { $_ * 2 } @data; 

    close($file); 
} 
0

जीसीसी :: TranslationUnit उदाहरण के लिए जो इसे सी में कर देगा http://gist.github.com/395160 से hparse.pl :: DynaLib, और अभी तक नहीं लिखा ctypes भी देखते हैं। यह पार्स एफएफआई के लिए काम करता है, और कनवर्ट :: बाइनरी :: सी के विपरीत नंगे structs नहीं। hparse केवल functs args के रूप में उपयोग किए जाने पर structs जोड़ देगा।

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