문제

문자열에서 C 및 C++ 주석을 제거하는 Python 코드를 찾고 있습니다.(문자열에 전체 C 소스 파일이 포함되어 있다고 가정합니다.)

Regex를 사용하여 하위 문자열을 .match()할 수 있다는 것을 알고 있지만 중첩이 해결되지 않습니다. /*, 또는 // 안에 /* */.

이상적으로는 어색한 경우를 적절하게 처리하는 순진하지 않은 구현을 선호합니다.

도움이 되었습니까?

해결책

당신이 익숙한 지 모르겠습니다 sed, UNIX 기반 (그러나 Windows-Available) 텍스트 구문 분석 프로그램이지만 SED 스크립트를 찾았습니다. 여기 파일에서 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 Windows에서도 사용할 수 있으며 대부분의 GNU/Linux 배포판 및 Mac 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는 깃발을 가져 가지 않았으므로 먼저 패턴을 컴파일해야했습니다.

edit2 : 문자 리터럴이 추가되었습니다. 그렇지 않으면 문자열 구분 기자로 인식되는 인용문이 포함되어 있기 때문입니다.

edit3 : 법적 표현이있는 경우를 수정했습니다 int/**/x=5; 될 것입니다 intx=5; 댓글을 공간으로 바꾸어 빈 문자열이 아닌 공간으로 바꾸어 컴파일하지 않습니다.

C (및 C ++) 주석을 중첩 할 수 없습니다. 정규 표현식은 잘 작동합니다.

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

"단일 라인"플래그가 필요합니다 (Re.S) C 댓글이 여러 줄에 걸쳐있을 수 있기 때문입니다.

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

이 코드는 작동해야합니다.

/편집 : 위의 코드는 실제로 라인 엔딩에 대해 가정합니다! 이 코드는 Mac 텍스트 파일에서 작동하지 않습니다. 그러나 이것은 비교적 쉽게 개정 될 수 있습니다.

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

이 정규식은 라인 엔딩에 관계없이 모든 텍스트 파일에서 작동해야합니다 (Windows, UNIX 및 MAC 라인 엔딩 커버).

/편집 : Mizardx와 Brian (주석에서)은 문자열 처리에 대해 유효한 언급을했습니다. 위의 정규식은 문자열에 대한 추가 처리가있는 구문 분석 모듈에서 뽑혀 있기 때문에 완전히 잊어 버렸습니다. Mizardx의 솔루션은 매우 잘 작동하지만 이중 인용 문자열 만 처리합니다.

C에서는 주석이 처리되기 전에 백 슬래시-뉴 라인이 제거되고 그 전에 트리 그라프가 처리되기 전에 제거되었다는 것을 잊지 마십시오 (? SCC (Strip C/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 */

이것은 트리 그라프를 설명하지 않습니다. 라인 끝에 여러 개의 백 슬래시를 가질 수 있지만 라인 스 플라이 싱은 얼마나 많은지에 대해 신경 쓰지 않지만 후속 처리는 가능합니다. 이 모든 사례를 처리하기 위해 단일 정규식을 작성하는 것은 사소한 일입니다 (그러나 불가능한 것과 다릅니다).

이 게시물은 Markus Jarderot의 게시에 대한 의견으로 Atikat이 설명한 Markus Jarderot의 코드 개선에 대한 코딩 된 버전을 제공합니다. (원래 코드를 제공해 주셔서 감사합니다. 많은 작업이 절약되었습니다.)

개선 사항을 다소 완전히 설명하기 위해 : 개선은 라인 번호를 그대로 유지합니다. (이것은 c/c ++ 주석을 대체하는 문자열에서 Newline 문자를 그대로 유지함으로써 이루어집니다.)

이 버전의 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)

문자열 리터럴에 주석 구문과 일치하는 하위 시퀀스가 ​​포함된 경우와 같은 일부 상황에서는 정규식 사례가 적용되지 않습니다.이 문제를 처리하려면 구문 분석 트리가 필요합니다.

당신은 활용할 수 있습니다 py ++ GCC로 C ++ 소스를 구문 분석합니다.

PY ++는 휠을 재발 명하지 않습니다. GCC C ++ 컴파일러를 사용하여 C ++ 소스 파일을 구문 분석합니다. 보다 정확하게 도구 체인은 다음과 같습니다.

소스 코드는 GCC-XML로 전달됩니다 GCC-XML은 GCC C ++ 컴파일러로 전달됩니다. GCC-XML은 GCC의 내부 표현에서 C ++ 프로그램의 XML 설명을 생성합니다. py ++는 pygccxml 패키지를 사용하여 GCC-XML 생성 파일을 읽습니다. 결론 - 모든 선언이 올바르게 읽히도록 확신 할 수 있습니다.

아니면 아마도 아닙니다. 어쨌든, 이것은 사소한 구문 분석이 아닙니다.

@ re 기반 솔루션 - 입력 (예 : 매크로 없음)을 제한하지 않는 한 가능한 모든 '어색한'사례를 올바르게 처리하는 RE를 찾을 수는 없습니다. 방탄 솔루션의 경우 실제 문법을 활용하는 것보다 선택이 없습니다.

파이썬 솔루션이 아니라 죄송하지만 C/C ++ 전처리 업체와 같이 주석을 제거하는 방법을 이해하는 도구를 사용할 수도 있습니다. GNU CPP가 다음은 다음과 같습니다 그렇게합니다.

cpp -fpreprocessed foo.c

비 파이썬 답변도 있습니다 : 프로그램 사용 Stripcmt:

StripCMT는 C, C ++ 및 Java 소스 파일에서 주석을 제거하기 위해 C로 작성된 간단한 유틸리티입니다. UNIX 텍스트 처리 프로그램의 웅대 한 전통에서는 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")

이것은 하위 프로세스와 CPP 전 처리기의 조합입니다. 내 프로젝트의 경우 "Util"이라는 유틸리티 클래스가있어 사용/필요한 다양한 도구를 유지합니다.

이 작업을 완벽하게 수행하기 위해 구문 분석 트리가 필요하지는 않지만 실제로 컴파일러의 프론트 엔드에 의해 생성되는 것과 동등한 토큰 스트림이 필요합니다. 그러한 토큰 스트림은 반드시 선정 주석 시작, 문자열에서 주석 시작, 트리 그라프 정규화 등과 같은 모든 이상함을 반드시 처리해야합니다. 토큰 스트림이있는 경우 주석을 삭제하는 것이 쉽습니다. (실제 구문 분석 트리를 생성하는 실제 구문 분석기의 프론트 엔드와 같은 토큰 스트림을 정확하게 생성하는 도구가 있습니다. :).

토큰이 정기적 인 표현으로 개별적으로 인식된다는 사실은 원칙적으로 Lexemes 주석을 선택할 정규 표현을 쓸 수 있음을 시사합니다. 토큰 화제에 대한 세트 정규 표현의 실제 복잡성 (적어도 우리가 쓴 것)은 실제로 이것을 할 수 없다는 것을 암시합니다. 개별적으로 쓰는 것은 충분히 어려웠습니다. 당신이 완벽하게하고 싶지 않다면, 위의 대부분의 re 솔루션은 괜찮습니다.

지금, 코드 oppuscator를 구축하지 않는 한 스트립 댓글이 저를 넘어서고 싶을 것입니다. 이 경우 완벽하게 올바르게해야합니다.

최근에 교수가 코드 검토를 위해 그에게 제출하기 전에 교수가 소스 코드에서 Javadoc을 제거 해야하는 수업을 들었을 때이 문제를 해결했습니다. 우리는 이것을 여러 번해야했지만 Javadoc HTML 파일도 생성해야했기 때문에 Javadoc을 영구적으로 제거 할 수 없었습니다. 여기에 트릭을 수행하기 위해 만든 작은 파이썬 스크립트가 있습니다. javadoc은 /**로 시작하여* /로 끝나기 때문에 스크립트는 이러한 토큰을 찾지만 스크립트를 수정하여 귀하의 요구에 맞게 수정할 수 있습니다. 또한 블록 주석이 끝나는 단일 줄 블록 주석과 사례를 처리하지만 블록 주석 끝과 동일한 줄에 여전히 작성되지 않은 코드가 있습니다. 이게 도움이 되길 바란다!

경고 :이 스크립트는 전달 된 파일의 내용을 수정하고 원본 파일에 저장합니다. 다른 곳에 백업을하는 것이 현명 할 것입니다.

#!/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