2014-09-09 2 views
1

Я пытаюсь найти комментарии стиля c в файле c, но имейте проблемы, если там будет // внутри цитат. Это файл:Python Regex reading in c style comments

/*My function 
is great.*/ 
int j = 0//hello world 
void foo(){ 
    //tricky example 
    cout << "This // is // not a comment\n"; 
} 

он будет соответствовать этому cout. Это то, что я до сих пор (я могу соответствовать/**/комментарии уже)

fp = open(s) 

p = re.compile(r'//(.+)') 
txt = p.findall(fp.read()) 
print (txt) 
+5

Невозможно разобрать C с регулярным выражением. Возможно, используйте что-то еще, например GCC-XML или front-end CLang. –

+0

Я представляю его как txt-файл или как строку – CRS

+2

Ваше воображение не меняет реальности. В этом случае на самом деле это можно сделать с регулярным выражением (очень гладким, Casimir!), Но бывают случаи, когда регулярные выражения имеют либо большую сложность, либо плоские выходы не могут этого сделать (сопоставление скобок является классическим примером, если только вы используя, например, Oniguruma, который имеет очень нерегулярные расширения). Это как: «Могу ли я есть суп с вилкой? Я представляю это как еду». Что вы можете. Это действительно лучший способ? – Amadan

ответ

4

Первого шаг заключается в выявлении случаев, когда // или /* не должен быть истолкован как начало комментария подстроки. Например, когда они находятся внутри строки (между кавычками). Для того, чтобы избежать содержаний в кавычках (или других вещах), хитрость заключается в том, чтобы поместить их в захвате группы и вставить обратную ссылку в шаблоне замены:

картина:

("(?:[^"\\]|\\[\s\S])*"|'(?:[^'\\]|\\[\s\S])*')|//.*|/\*(?:[^*]|\*(?!/))*\*/ 

замена:

\1 

online demo

Поскольку цитируемые части поиска первых, каждый раз, когда вы находите // или /*...*/, вы можете быть убедитесь, что вы не внутри строки.

Обратите внимание, что шаблон является добровольным неэффективен, чтобы сделать его легче понять, чтобы сделать его более эффективным можно переписать так:

("(?=((?:[^"\\]+|\\[\s\S])*))\2"|'(?=((?:[^'\\]+|\\[\s\S])*))\3')|//.*|/\*(?=((?:[^*]+|\*(?!/))*))\4\*/ 

(?=(something+))\1 только способ эмулировать atomic group(?>something+)

online demo

Итак, если вы только хотите, чтобы найти комментарии (и не удалять их), наиболее удобно будет поместить комментарии часть шаблона в Captur e и проверить, не является ли он пустым. Следующая картина была udapted (после того, как Джонатан Леффлера комментарий) обрабатывать триграф ??/, что интерпретируется как символ обратной косой черты препроцессором (я предполагаю, что код написан не для -trigraphs option) и обрабатывать обратную косую черту за которым следует символ новой строки, что позволяет форматировать одну строку на несколько строк:

fp = open(s) 

p = re.compile(r'''(?x) 
(?=["'/])  # trick to make it faster, a kind of anchor 
(?: 
    "(?=((?:[^"\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\1" # double quotes string 
    | 
    '(?=((?:[^'\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\2' # single quotes string 
    | 
    (
     /(?:(?:\?\?/|\\)\n)*/(?:.*(?:\?\?|\\)/\n)*.* # single line comment 
     | 
     /(?:(?:\?\?/|\\)\n)*\*      # multiline comment 
     (?=((?:[^*]+|\*+(?!(?:(?:\?\?/|\\)\n)*/))*))\4 
     \*(?:(?:\?\?/|\\)\n)*/    
    ) 
) 
''') 

for m in p.findall(fp.read()): 
    if (m[2]):  
     print m[2] 

Эти изменения не влияют на эффективность картины, так как основная работа для регулярных выражений, чтобы найти позиции, которые начинаются с цитаты или его слэш. Эта задача упрощается благодаря наличию lookahead в начале шаблона (?=["'/]), который позволяет внутренним оптимизациям быстро находить первый символ.

Другая оптимизация - использование эмулируемых атомных групп, что сводит назад к минимуму и позволяет использовать жадные кванторы внутри повторяющихся групп.

NB: шанс отсутствия синтаксиса heredoc в C!

+1

Очень хорошо, и охватывает подавляющее большинство фактических комментариев. Компилятор должен быть более осторожным. Он должен учитывать, следует ли обрабатывать триграфы (поскольку обратная косая черта может быть написана '?? /'). Обратная косая черта в конце строки, содержащая комментарий '//', продолжает комментарий к следующей строке. Более того, даже если никто в здравом уме не написал код с '/', отделенным от второй '/' или '*' одной или несколькими парами обратной косой черты, это все равно начало комментария. «*» Следует за одной или несколькими парами обратной линии обратной связи, а затем '/' - это конец многострочного комментария. Ик! –

+0

@JonathanLeffler: Приятно знать, я попытаюсь создать шаблон для обработки этих синтаксических особенностей. Можно ли интерпретировать триграммы и внутри строки? –

+0

@ CasimiretHippolyte..juste один вопрос, это быстрее (? = ["'/]) Или это (? =. *? ["' /]) И почему? Спасибо –

1

Метод re.findall Python в основном работает так же, как и большинство лексеров: он последовательно возвращает самый длинный матч, начиная с предыдущего окончания.Все, что требуется для получения дизъюнкции всех лексических моделей:

(<pattern 1>)|(<pattern 2>)|...|(<pattern n>) 

В отличии от большинства лексеров, он не требует спичек, чтобы быть смежными, но это не существенная разница, так как вы всегда можете просто добавить (.) как последний шаблон, чтобы сопоставить все иначе непревзойденные символы в отдельности.

Важной особенностью re.findall является то, что если в регулярном выражении есть группы, тогда будут возвращаться только группы. Следовательно, вы можете исключить альтернативы, просто оставляя круглые скобки, или изменяя их не-захвата скобки:

(<pattern 1>)|(?:<unimportant pattern 2>)|(<pattern 3) 

Имея это в виду, давайте взглянем на то, как разметить C достаточно, чтобы признать комментарии. Мы должны иметь дело с:

  1. однострочные комментарии: // Comment
  2. Многоканальный Комментарии: /* Comment */
  3. двойные кавычки: "Might include escapes like \n"
  4. Single цитируемый персонаж: '\t'
  5. (см ниже для еще нескольких раздражающих случаев)

Имея это в виду, давайте c reate regexen для каждого из вышеперечисленных.

  1. Две косые следует ничего, кроме символа новой строки: //[^\n]*
  2. Это регулярное выражение является утомительным объяснить: /*[^*]*[*]+(?:[^/*][^*]*[*]+)*/ Обратите внимания, что она использует (?:...), чтобы избежать захвата неоднократной группы.
  3. Цитата, любое повторение символа, отличное от цитирования и обратной косой черты, или обратная косая черта, сопровождаемая любым символом. Это не точное определение последовательности побега, но это достаточно хорошо, чтобы определить, когда " завершает строку, которая нас интересует: "(?:[^"\\]|\\.*)"
  4. же, как (3), но с одинарными кавычками: '(?:[^'\\]|\\.)*'

Наконец, цель состояла в том, чтобы найти текст комментариев в стиле с Итак, нам необходимо, чтобы избежать захватов в любой из других групп Следовательно:..

p = re.compile('|'.join((r"(//[^\n])*" 
         ,r"/*[^*]*[*]+(?:[^/*][^*]*[*]+)*/" 
         ,'"'+r"""(?:[^"\\]|\\.)*"""+'"' 
         ,r"'(?:[^'\\]|\\.)*'"))) 
return [c[2:] for c in p.findall(text) if c] 

Abo ве, я оставил некоторые неясные случаи, которые вряд ли возникнут:

  1. В качестве #include <...> директивы, то <...> существу строка.Теоретически, он может содержать кавычки или последовательности, которые выглядят как комментарии, но на практике вы никогда не увидите:

    #include </*This looks like a comment but it is a filename*/> 
    
  2. линия, которая заканчивается \ продолжается на следующей строке; \ и следующий символ новой строки просто удаляются с ввода. Это происходит до того выполняется любое лексическое сканирование, так что следующий является совершенно законным комментарием (на самом деле два комментариев):

    /\ 
    **************** Surprise! **************\ 
    ////////////////////////////////////////// 
    
  3. Для того, чтобы выше хуже, триграф ??/ таким же, как \ , и что замена происходит до обработки продолжения.

    /************************************//??/ 
    **************** Surprise! ************??/ 
    ////////////////////////////////////////// 
    

    Вне конкурсов обфускации никто не использует триграфы. Но они все еще в стандарте. Самый простой способ справиться с обоими из этих вопросов было бы предварительное сканирование строки:

    return [c[2:] 
         for c in p.findall(text.replace('//?','\\').replace('\\\n','')) 
         if c] 
    

Единственный способ справиться с #include <...> вопрос, если вы действительно заботились о нем, можно было бы добавить еще один узор, что-то вроде #define\s*<[^>\n]*>.

+0

«Никто на самом деле не использует триграфы»: Теперь я буду писать триграф каждый день в моей жизни. –