2008-10-27 18 views
37

मैं पाइथन कोड की तलाश में हूं जो एक स्ट्रिंग से सी और सी ++ टिप्पणियों को हटा देता है। (मान लें स्ट्रिंग एक पूरी सी स्रोत फ़ाइल है।)सी और सी ++ टिप्पणियों को हटाने के लिए पायथन स्निपेट

मुझे लगता है कि मैं .match सकता है() एक Regex साथ सबस्ट्रिंग, लेकिन वह /* घोंसला बनाने से, या एक // एक /* */ अंदर होने का समाधान नहीं करता।

आदर्श रूप से, मैं एक गैर-निष्पक्ष कार्यान्वयन पसंद करूंगा जो अजीब मामलों को सही ढंग से संभालता है।

+0

पृथ्वी पर क्यों * स्रोत से टिप्पणियां * हटाना चाहते हैं ??? – QuantumPete

+2

@QuantumPete, पठनीयता और समझ में सुधार करने के लिए। सबसे तेज़ तरीका रंगीन संपादक का उपयोग करना और पृष्ठभूमि रंग के बराबर टिप्पणी रंग सेट करना है। –

+1

@QuantumPete या क्योंकि हम बाद के प्रोसेसर के लिए स्रोत कोड प्रीप्रोसेस करने की कोशिश कर रहे हैं जो सैनी टिप्पणियां नहीं लेता –

उत्तर

8

अगर आप sed से परिचित हैं मैं नहीं जानता, यूनिक्स आधारित (लेकिन विंडोज उपलब्ध है) पाठ पार्स कार्यक्रम है, लेकिन मैं एक sed स्क्रिप्ट here जो एक फ़ाइल से C/C++ टिप्पणियों को हटा देगा पाया है । यह बहुत स्मार्ट है; उदाहरण के लिए, यह ध्यान नहीं देगा '//' और '/ *' अगर एक स्ट्रिंग घोषणा, आदि अजगर के भीतर से में पाया, यह निम्न कोड का उपयोग किया जा सकता है:

import subprocess 
from cStringIO import StringIO 

input = StringIO(source_code) # source_code is a string with the source code. 
output = StringIO() 

process = subprocess.Popen(['sed', '/path/to/remccoms3.sed'], 
    input=input, output=output) 
return_code = process.wait() 

stripped_code = output.getvalue() 

इस कार्यक्रम में, source_code है वेरिएबल सी/सी ++ स्रोत कोड धारण करते हैं, और अंत में stripped_code टिप्पणियों के साथ सी/सी ++ कोड धारण करेगा। बेशक, यदि आपके पास डिस्क पर फ़ाइल है, तो आपके पास input और output वेरिएबल्स फ़ाइल फ़ाइलों को इंगित कर सकते हैं (input रीड-मोड में, output लेखन-मोड में)। remccoms3.sed उपरोक्त लिंक से फ़ाइल है, और इसे डिस्क पर पढ़ने योग्य स्थान में सहेजा जाना चाहिए। sed विंडोज पर भी उपलब्ध है, और अधिकांश जीएनयू/लिनक्स डिस्ट्रोज़ और मैक ओएस एक्स पर डिफ़ॉल्ट रूप से स्थापित होता है।

यह शायद शुद्ध पायथन समाधान से बेहतर होगा; पहिया को फिर से शुरू करने की कोई ज़रूरत नहीं है।

+17

सेड का उपयोग करके अपनी पायथन स्क्रिप्ट पर एक अतिरिक्त स्क्रिप्ट और टूल निर्भरता पेश न करें। दोनों या नहीं, सेड या पायथन चुनें। –

24

सी (और सी ++) टिप्पणियां घोंसला नहीं जा सकती हैं। रेग्युलर एक्सप्रेशंस अच्छी तरह से काम:

//.*?\n|/\*.*?\*/ 

यह "सिंगल लाइन" झंडा (Re.S) की आवश्यकता है क्योंकि एक सी टिप्पणी कई पंक्तियों अवधि कर सकते हैं।

def stripcomments(text): 
    return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S) 

यह कोड काम करना चाहिए।

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

//.*?(\r\n?|\n)|/\*.*?\*/ 

यह नियमित अभिव्यक्ति सभी पाठ फ़ाइलों पर काम करना चाहिए, उनकी लाइन अंत की परवाह किए बिना (शामिल किया गया है विंडोज, यूनिक्स और मैक लाइन अंत)।

/संपादित करें: मिज़ार्डएक्स और ब्रायन (टिप्पणियों में) तारों के संचालन के बारे में एक वैध टिप्पणी की। मैं पूरी तरह से इसके बारे में भूल गया क्योंकि उपरोक्त रेगेक्स को एक पार्सिंग मॉड्यूल से हटा दिया गया है जिसमें तारों के लिए अतिरिक्त हैंडलिंग है। MizardX के समाधान को बहुत अच्छी तरह से काम करना चाहिए, लेकिन यह केवल डबल-उद्धृत तारों को संभालता है।

+3

1. '\ n ',' \ r \ n 'के बजाय' $ 'और re.MULTILINE का उपयोग करें, आदि – jfs

+0

यह बैकस्लैश में समाप्त होने वाली रेखा के मामले को संभाल नहीं करता है, जो एक निरंतर रेखा इंगित करता है, लेकिन यह मामला बेहद दुर्लभ है –

+0

आपने re.sub में प्रतिस्थापन रिक्त स्ट्रिंग को याद किया है। इसके अलावा, यह तारों के लिए काम नहीं करेगा। उदाहरण के लिए। 'स्ट्रिंग uncPath = "// some_path" पर विचार करें;' या 'चार ऑपरेटर [] = "/ * + -";' भाषा पार्सिंग के लिए, मुझे लगता है कि आप असली पार्सर का उपयोग कर सबसे अच्छे हैं। – Brian

3

आप जीसीसी के साथ सी ++ स्रोत को पार्स करने के लिए py++ का लाभ उठाने में सक्षम हो सकते हैं।

Py++ does not reinvent the wheel. It uses GCC C++ compiler to parse C++ source files. To be more precise, the tool chain looks like this:

source code is passed to GCC-XML GCC-XML passes it to GCC C++ compiler GCC-XML generates an XML description of a C++ program from GCC's internal representation. Py++ uses pygccxml package to read GCC-XML generated file. The bottom line - you can be sure, that all your declarations are read correctly.

या शायद नहीं। परवाह किए बिना, यह एक मामूली पार्स नहीं है।

@ आरई आधारित समाधान - आपको एक आरई खोजने की संभावना नहीं है जो सभी संभावित 'अजीब' मामलों को सही तरीके से संभालती है, जब तक कि आप इनपुट को बाधित न करें (उदा। कोई मैक्रोज़)। बुलेटप्रूफ समाधान के लिए, वास्तव में असली व्याकरण का लाभ उठाने से आपके पास कोई विकल्प नहीं है।

+0

इसके अलावा, जैसा कि एलेक्स कोवेन्ट्री का उल्लेख है, सरल रेगेक्सिस स्ट्रिंग अक्षर को नली करेगा जो टिप्पणी मार्कर (जो पूरी तरह से कानूनी है) शामिल है। –

71

यह सी ++ - शैली टिप्पणियों, सी-शैली टिप्पणियों, तारों और सरल घोंसले को संभालता है।

def comment_remover(text): 
    def replacer(match): 
     s = match.group(0) 
     if s.startswith('/'): 
      return " " # note: a space and not an empty string 
     else: 
      return s 
    pattern = re.compile(
     r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', 
     re.DOTALL | re.MULTILINE 
    ) 
    return re.sub(pattern, replacer, text) 

स्ट्रिंग्स को शामिल करने की आवश्यकता है, क्योंकि उनके अंदर टिप्पणी-चिह्नक कोई टिप्पणी शुरू नहीं करते हैं।

संपादित करें: re.sub ने कोई झंडे नहीं लिया, इसलिए पहले पैटर्न को संकलित करना पड़ा।

संपादित 2: जोड़ा चरित्र अक्षर, क्योंकि उनमें उद्धरण शामिल हो सकते हैं जिन्हें अन्यथा स्ट्रिंग डिलीमीटर के रूप में पहचाना जाएगा।

Edit3: मामले में जहां एक कानूनी अभिव्यक्ति int/**/x=5;intx=5; जो संकलन नहीं होगा, हो जाएगा एक अंतरिक्ष बल्कि उसके बाद कोई रिक्त स्ट्रिंग के साथ टिप्पणी की जगह फिक्स्ड।

+0

यह बच निकला नहीं है "तारों में वर्ण। उदाहरण: char * some_punctuation_chars ="। \ "/ *";/* टिप्पणी */ – Brian

+0

हां यह करता है। '\\।' किसी भी भागने वाले चार से मेल खाएगा, जिसमें \ \ "' –

+0

डी ओह - आप सही हैं - मैं उस भाग को गलत तरीके से पढ़ता हूं। – Brian

6

भूल जाते हैं कि सी में, बैकस्लैश-न्यू लाइन टिप्पणी संसाधित होने से पहले समाप्त हो जाता है, और trigraphs कि पहले कार्रवाई की जाती है (क्योंकि ??/बैकस्लैश के लिए trigraph है) है। मैं एक सी कार्यक्रम एस सी सी (पट्टी C/C++ टिप्पणियाँ) कहा जाता है, और यहां परीक्षण कोड का हिस्सा है ...

" */ /* SCC has been trained to know about strings /* */ */"! 
"\"Double quotes embedded in strings, \\\" too\'!" 
"And \ 
newlines in them" 

"And escaped double quotes at the end of a string\"" 

aa '\\ 
n' OK 
aa "\"" 
aa "\ 
\n" 

This is followed by C++/C99 comment number 1. 
// C++/C99 comment with \ 
continuation character \ 
on three source lines (this should not be seen with the -C fla 
The C++/C99 comment number 1 has finished. 

This is followed by C++/C99 comment number 2. 
/\ 
/\ 
C++/C99 comment (this should not be seen with the -C flag) 
The C++/C99 comment number 2 has finished. 

This is followed by regular C comment number 1. 
/\ 
*\ 
Regular 
comment 
*\ 
/
The regular C comment number 1 has finished. 

/\ 
\/ This is not a C++/C99 comment! 

This is followed by C++/C99 comment number 3. 
/\ 
\ 
\ 
/But this is a C++/C99 comment! 
The C++/C99 comment number 3 has finished. 

/\ 
\* This is not a C or C++ comment! 

This is followed by regular C comment number 2. 
/\ 
*/ This is a regular C comment *\ 
but this is just a routine continuation *\ 
and that was not the end either - but this is *\ 
\ 
/
The regular C comment number 2 has finished. 

This is followed by regular C comment number 3. 
/\ 
\ 
\ 
\ 
* C comment */ 

यह trigraphs उदाहरण देकर स्पष्ट नहीं है। ध्यान दें कि आपके पास लाइन के अंत में एकाधिक बैकस्लैश हो सकते हैं, लेकिन लाइन स्प्लिसिंग इस बात पर परवाह नहीं करता कि कितने हैं, लेकिन बाद की प्रक्रिया हो सकती है। इत्यादि। इन सभी मामलों को संभालने के लिए एक एकल रेगेक्स लिखना गैर-तुच्छ होगा (लेकिन यह असंभव से अलग है)।

+0

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

4

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

+0

यह एकमात्र प्रतिक्रिया है जिसमें बदसूरत हैक शामिल नहीं है। – sim642

0

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

तथ्य यह है कि नियमित अभिव्यक्तियों द्वारा टोकन को व्यक्तिगत रूप से पहचाना जाता है, यह बताता है कि आप सिद्धांत रूप से नियमित अभिव्यक्ति लिख सकते हैं जो टिप्पणी लेक्सम को चुन लेगा। टोकननाइज़र के लिए नियमित रूप से अभिव्यक्तियों की वास्तविक जटिलता (कम से कम जिसे हमने लिखा है) सुझाव देता है कि आप इसे अभ्यास में नहीं कर सकते हैं; उन्हें व्यक्तिगत रूप से लिखना काफी मुश्किल था। यदि आप इसे पूरी तरह से नहीं करना चाहते हैं, तो, ऊपर दिए गए अधिकांश आरई समाधान ठीक हैं।

अब, क्यों आप चाहते हैं कि स्ट्रिप टिप्पणियां मेरे बाहर हों, जब तक आप कोई कोड obfuscator नहीं बना रहे हों। इस मामले में, आपको इसे बिल्कुल सही करना होगा।

1

मुझे खेद है कि यह पाइथन समाधान नहीं है, लेकिन आप एक ऐसे टूल का भी उपयोग कर सकते हैं जो आपके सी/सी ++ प्रीप्रोसेसर की तरह टिप्पणियों को कैसे हटा सकता है। यहां बताया गया है कि जीएनयू सीपीपी does it कैसे।

cpp -fpreprocessed foo.c 
+2

अच्छी सोच, हालांकि यह एक शर्म की बात है कि यह सिर्फ टिप्पणियों को हटाने से ज्यादा करता है! – frankster

1

वहाँ भी एक गैर अजगर जवाब है:

StripCmt is a simple utility written in C to remove comments from C, C++, and Java source files. In the grand tradition of Unix text processing programs, it can function either as a FIFO (First In - First Out) filter or accept arguments on the commandline.

-1

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

चेतावनी: यह स्क्रिप्ट फाइलों की सामग्री को संशोधित करती है और उन्हें मूल फ़ाइलों में सहेजती है। यह कहीं और

#!/usr/bin/python 
""" 
A simple script to remove block comments of the form /** */ from files 
Use example: ./strip_comments.py *.java 
Author: holdtotherod 
Created: 3/6/11 
""" 
import sys 
import fileinput 

for file in sys.argv[1:]: 
    inBlockComment = False 
    for line in fileinput.input(file, inplace = 1): 
     if "/**" in line: 
      inBlockComment = True 
     if inBlockComment and "*/" in line: 
      inBlockComment = False 
      # If the */ isn't last, remove through the */ 
      if line.find("*/") != len(line) - 3: 
       line = line[line.find("*/")+2:] 
      else: 
       continue 
     if inBlockComment: 
      continue 
     sys.stdout.write(line) 
+0

यह निश्चित रूप से विफल रहता है यदि स्ट्रिंग के भीतर '//' या '/ *' है, या '/' सीमित नियमित अभिव्यक्ति के भीतर। – robocat

+0

नहीं, ऐसा नहीं है। यह विवरण में बताए गए अनुसार '/ ** * /' शैली जावा ब्लॉक टिप्पणियों की तलाश में है। यह '//' या '/ *' या यहां तक ​​कि '/' को संभाल नहीं करता है ... यह सही नहीं है, लेकिन यह "असफल" नहीं होता है, केवल आपके द्वारा बताए गए मामलों को अनदेखा करता है। यह किसी भी चीज़ की तलाश में किसी के लिए सिर्फ एक संदर्भ था। – slottermoser

6

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

कुछ हद तक सुधार का वर्णन करने के लिए: सुधार लाइन नंबरिंग को बरकरार रखता है। (यह तारों में अक्षरों के अक्षरों को बरकरार रखने के द्वारा किया जाता है जिसके द्वारा सी/सी ++ टिप्पणियां प्रतिस्थापित की जाती हैं।)

सी/सी ++ टिप्पणी निष्कासन समारोह का यह संस्करण उपयुक्त है जब आप अपने उपयोगकर्ताओं को त्रुटि संदेश उत्पन्न करना चाहते हैं (उदाहरण के लिए त्रुटियों को पार्स करना) जिसमें रेखा संख्याएं होती हैं (यानी मूल पाठ के लिए मान्य लाइन संख्या)।

import re 

def removeCCppComment(text) : 

    def blotOutNonNewlines(strIn) : # Return a string containing only the newline chars contained in strIn 
     return "" + ("\n" * strIn.count('\n')) 

    def replacer(match) : 
     s = match.group(0) 
     if s.startswith('/'): # Matched string is //...EOL or /*...*/ ==> Blot out all non-newline chars 
      return blotOutNonNewlines(s) 
     else:     # Matched string is '...' or "..." ==> Keep unchanged 
      return s 

    pattern = re.compile(
     r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', 
     re.DOTALL | re.MULTILINE 
    ) 

    return re.sub(pattern, replacer, text) 
1

मेरे लिए काम किया है:

from subprocess import check_output 

class Util: 
    def strip_comments(self,source_code): 
    process = check_output(['cpp', '-fpreprocessed', source_code],shell=False) 
    return process 

if __name__ == "__main__": 
    util = Util() 
    print util.strip_comments("somefile.ext") 

यह उपप्रक्रिया और सीपीपी पूर्वप्रक्रमक का एक संयोजन है। मेरे प्रोजेक्ट के लिए मेरे पास "यूटिल" नामक एक यूटिलिटी क्लास है जिसे मैं विभिन्न टूल/उपयोग करता हूं।

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