سؤال

أنا أبحث عن كود Python الذي يزيل تعليقات C وC++ من السلسلة.(افترض أن السلسلة تحتوي على ملف مصدر C بأكمله.)

أدرك أنه يمكنني .match() سلاسل فرعية مع Regex، ولكن هذا لا يحل التداخل /*, ، أو وجود // داخل /* */.

من الناحية المثالية، أفضل التنفيذ غير الساذج الذي يتعامل بشكل صحيح مع الحالات المحرجة.

هل كانت مفيدة؟

المحلول

وأنا لا أعرف ما إذا كنت على دراية sed، ويونيكس القائم على (ولكن ويندوز متوفر) برنامج تحليل النص، ولكني وجدت السيناريو الحوار الاقتصادي الاستراتيجي <لأ href = "HTTP: //sed.sourceforge صافي / grabbag / مخطوطات / remccoms3.sed "يختلط =" noreferrer "> هنا الذي سيزيل 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 هو المتغير عقد C / C ++ شفرة المصدر، وstripped_code في نهاية المطاف سوف يعقد C / C ++ كود مع التعليقات التي تمت إزالتها. وبطبيعة الحال، إذا كان لديك ملف على القرص، هل يمكن أن يكون المتغيرات input وoutput تكون مؤشرات الملفات تشير إلى تلك الملفات (input في قراءة الوضع، output في الكتابة واسطة). remccoms3.sed هو الملف من الرابط أعلاه، وكان ينبغي حفظها في مكان مقروء على القرص. sed هو أيضا متوفرة على ويندوز، وتأتي مثبتة بشكل افتراضي على معظم توزيعات جنو / لينكس وماك OS X.

وهذا من المحتمل أن يكون أفضل من الحل بيثون النقي. لا حاجة لإعادة اختراع العجلة.

نصائح أخرى

يتعامل هذا مع التعليقات بنمط C++ والتعليقات بنمط C والسلاسل والتداخل البسيط لها.

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: تمت إضافة حرف حرفي، نظرًا لأنها يمكن أن تحتوي على علامات اقتباس يمكن التعرف عليها كمحددات للسلسلة.

تحرير 3: تم إصلاح الحالة التي يكون فيها التعبير القانوني int/**/x=5; قد يصبح intx=5; والتي لن يتم تجميعها، عن طريق استبدال التعليق بمسافة بدلاً من سلسلة فارغة.

وC (وC ++) التعليقات التي لا يمكن أن تتداخل. التعابير العادية تعمل بشكل جيد:

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

وهذا يتطلب "سطر واحد" العلم (Re.S) لأن C تعليق يمكن أن تمتد لعدة أسطر.

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

ويجب أن تعمل هذه التعليمات البرمجية.

و/ تحرير: لاحظ أن بلدي رمز أعلاه في الواقع يجعل افتراض حول خط النهايات! وهذا الرمز لا تعمل على ملف نصي ماك. ومع ذلك، يمكن تعديل هذا بسهولة نسبيا:

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

وهذا التعبير العادي يجب أن تعمل على جميع الملفات النصية، بغض النظر عن النهايات خطهم (يغطي ويندوز، يونكس وماك نهايات خط).

و/ تحرير: جعل MizardX وبراين (في تعليق) ملاحظة صحيحة حول التعامل مع السلاسل. لقد نسيت تماما عن ذلك لأن التقطه التعبير المعتاد أعلاه من وحدة إعراب له معالجة إضافية لسلاسل. حل MizardX يجب أن تعمل بشكل جيد جدا ولكنه يعالج فقط السلاسل بين علامات الاقتباس المزدوجة.

ولا ننسى أن في C، يتم التخلص مائل-السطر الجديد قبل أن يتم معالجتها التعليقات، وتتم معالجة trigraphs قبل ذلك (لأن ؟؟ / هو trigraph لمائل). لدي برنامج C يسمى SCC (قطاع 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. لاحظ أنه يمكن أن يكون مائلة عكسية متعددة في نهاية السطر، ولكن الربط الخط لا يهمه كم هناك، ولكن قوة معالجة لاحقة. الخ كتابة التعابير المنطقية واحد للتعامل مع جميع هذه الحالات سيكون غير تافهة (ولكن هذا يختلف من المستحيل).

وهذا الإعلان يوفر نسخة مشفرة من التحسن إلى رمز ماركوس Jarderot التي وصفت من قبل atikat، في تعليق على نشر ماركوس Jarderot ل. (شكرا على كل لتوفير رمز الأصلي، والتي وفرت لي الكثير من العمل).

لوصف تحسن إلى حد ما أكثر بالكامل: تحسين يحافظ على خط عددهم سليمة. (ويتم ذلك عن طريق الحفاظ على أحرف السطر سليمة في السلاسل التي وC / يتم استبدال تعليقات C ++).

وهذا الإصدار من وظيفة إزالة تعليق C / C ++ مناسب عندما تريد إنشاء رسائل الخطأ للمستخدمين (مثل أخطاء التوزيع) التي تحتوي على أرقام الأسطر (أرقام الأسطر أي صالحة للنص الأصلي).

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)

وحالات التعبير العادية سوف تسقط في بعض الحالات، مثل التي تحتوي على سلسلة حرفية ومتتالية جزئية والذي يطابق بناء الجملة للتعليق. كنت حقا بحاجة الى شجرة تحليل للتعامل مع هذا.

وكنت قد تكون قادرة على الاستفادة من الحمر ++ لتحليل C ++ المصدر مع دول مجلس التعاون الخليجي.

<اقتباس فقرة>   

السنة التحضيرية ++ لا إعادة اختراع العجلة. هذا   يستخدم مترجم دول مجلس التعاون الخليجي C ++ تحليل C ++   ملفات المصدر. لنكون أكثر دقة، و   سلسلة أداة تبدو مثل هذا:

     يتم تمرير

وشفرة المصدر لدول مجلس التعاون الخليجي XML   دول مجلس التعاون الخليجي XML يمررها إلى مترجم دول مجلس التعاون الخليجي C ++   دول مجلس التعاون الخليجي XML يولد وصف XML   برنامج C ++ من الداخلية في دول مجلس التعاون الخليجي   التمثيل. يستخدم الحمر ++ pygccxml   حزمة لقراءة دول مجلس التعاون الخليجي XML ولدت   ملف. بيت القصيد - يمكنك أن تكون   تأكد من أن جميع الإعلانات الخاصة بك   قراءة بشكل صحيح.

و، أو ربما لا. بغض النظر، وهذا ليس تحليل تافهة.

و @حلول RE مقرها - كنت من غير المرجح أن يجد RE الذي يعالج جميع الحالات 'حرج' ممكنة بشكل صحيح، إلا إذا كنت تقييد الإدخال (على سبيل المثال لا حدات الماكرو). للتوصل إلى حل للرصاص، هل حقا لا خيار من الاستفادة من قواعد حقيقية.

وأنا آسف هذا ليس حلا بيثون، ولكن يمكن أيضا استخدام أداة يفهم كيفية إزالة التعليقات، مثل الخاص بك C / C ++ المعالج. وهنا كيف GNU CPP يفعل .

cpp -fpreprocessed foo.c

وهناك أيضا إجابة غير بيثون: استخدام برنامج stripcmt :

<اقتباس فقرة>   

وStripCmt هو أداة بسيطة مكتوبة   في C إلى إزالة التعليقات من C، C ++،   وملفات المصدر جافا. في الكبرى   تقليد تجهيز النصوص يونكس   برامج، فإنه يمكن أن تعمل إما على شكل   FIFO (الأولى في - أولا خارج) مرشح أو   استعرض الحجج على فلكس.

وفيما يلي عملت بالنسبة لي:

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")

وهذا هو مزيج من فرعي أو جانبي والمعالج حزب الشعب الكمبودي. لمشروعي لدي فئة مساعدة تسمى "UTIL" أن أحافظ على أدوات مختلفة يمكنني استخدام / الحاجة.

وأنت لا تحتاج حقا شجرة تحليل للقيام بذلك تماما، ولكنك في الواقع بحاجة إلى ما يعادل تيار رمزي إلى ما تنتجه الأمامية المترجم ل. يجب أن مثل تيار رمزية necessarilyy رعاية جميع غرابة مثل خط استمرار بداية تعليق، تعليق البدء في سلسلة، trigraph التطبيع، وما إذا كان لديك تيار رمزية، وحذف التعليقات أمرا سهلا. (لدي الأداة التي تنتج مثل هذه بالضبط تيارات رمزية، و، وتخمين ما، والواجهة الأمامية للمحلل الحقيقي الذي ينتج تحليل حقيقي شجرة :).

والحقيقة أن الرموز يتم التعرف بشكل فردي عن طريق التعابير العادية تشير إلى أن كنت تستطيع، من حيث المبدأ، وكتابة التعبير العادية التي سوف تقتلع lexemes تعليق. تعقيد الحقيقي من التعبيرات العادية المحددة للtokenizer (على الأقل واحد كتبنا) يوحي لا يمكنك أن تفعل ذلك على أرض الواقع. الكتابة على حدة كان من الصعب بما فيه الكفاية. إذا كنت لا تريد أن تفعل ذلك تماما، حسنا، ثم، فإن معظم حلول RE هي على ما يرام أعلاه.

والآن، <م> لماذا كنت تريد تعليق الشريط هو خارج عن ارادتي، إلا إذا كنت بناء بفوسكاتور التعليمات البرمجية. في هذه الحالة، يكون لديك على حق تماما.

وركضت عبر هذه المشكلة مؤخرا عندما أخذت فئة حيث استاذ حاجة لنا لتجريد جافادوك من الشفرة المصدرية لدينا قبل تقديمه له لمراجعة التعليمات البرمجية. كان علينا أن نفعل ذلك عدة مرات، ولكننا لا يمكن أن مجرد إزالة جافادوك بشكل دائم لأننا كنا المطلوبة لإنشاء ملفات جافادوك أتش تي أم أل أيضا. هنا هو القليل بيثون السيناريو الذي أدليت به لتفعل خدعة. منذ بدء جافادوك مع / ** وينتهي * /، يبدو السيناريو لهذه الرموز، ولكن السيناريو يمكن تعديلها لتناسب احتياجاتك. كما أنه يتعامل مع خط واحد تعليق كتلة والحالات التي تنتهي كتلة تعليق ولكن لا يزال هناك قانون غير علق على نفس السطر مثل تعليق كتلة تنتهي. آمل أن يساعد هذا!

تحذير: هذه البرامج النصية بتعديل محتويات الملفات مرت في ويحفظ لهم الملفات الأصلية. قد يكون من الحكمة لديك نسخة احتياطية في مكان آخر

#!/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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top