2012-09-23 2 views
2

Я пытаюсь создать файл со всеми именами функций/enum/struct/etc из исходного файла. Для этого, я в данный момент пытается использовать sed сделать что-то вроде этого:Использование sed для удаления тел функций в файле C/C++

(исходный файл)

function add1 (int i) { 
    return i+1; 
} 

(выход СЕПГ)

function add1 (int i) { 
} 

Другими словами, я хотите удалить фактическое содержимое тела функции. До сих пор я не мог заставить его работать. Какие-либо предложения?

EDIT: Я пытался что-то вроде этого, но безуспешно (на данный момент я пытаюсь сделать только линии на теле функции пустой):

sed '/{/,/}/ s/.*//' 
+2

Вы должны объяснить, почему вы спрашиваете об этом. –

ответ

1

Вместо sed, всегда можно было использовать в режиме awk поле каждого символа (FS=""):

awk 'BEGIN { 
     RS = "\n" ; 
     FS = "" ; 
     d = 0 ; 
    } 

    { 
     for (i=1; i<=NF; i++) 
      if ($i == "{") { 
       d++ ; 
       if (d == 1) printf "{\n" 
      } else 
      if ($i == "}") { 
       d-- ; 
       if (d == 0) printf "}" 
      } else 
      if (d == 0) 
       printf "%s", $i ; 
     if (d == 0) printf "\n" 
    }' INPUT-FILE(s)... 

выше будет пропускать содержимое любых парных фигурных скобок, т.е.функции и структуры, инициализации массива и т. д. и вывод результата в стандартный вывод. Вы можете указать один или несколько файлов. (Если вы не укажете какие-либо файлы, он будет ожидать ввода со стандартного ввода.)

Как и сейчас, он будет запутаться в фигурных скобках в кавычках или комментариях. Это можно было бы зафиксировать точно так же, но это довольно быстро. Это всего лишь взломать, чтобы доставить вам удовольствие.

Я добавил точки с запятой (;), чтобы вы могли просто набить все в приведенном выше фрагменте на одной длинной командной строке.

Логика сценария очень проста. Он использует пустой разделитель полей (FS), так что каждый символ ввода будет своим собственным полем. Правило BEGIN запускается один раз перед обработкой любого ввода и устанавливает это. Для информации разработчика я также инициализирую d = 0, хотя это не обязательно для awk, так как предполагается, что неинициализированные переменные будут пустыми или нулевыми, если это необходимо. Он будет отслеживать текущую глубину привязки для каждого входного символа.

Второе сжатое выражение будет выполняться один раз за каждую запись. Поскольку я устанавливаю RS = "\n", каждая строка является отдельным выражением. Таким образом, он будет выполняться один раз для каждой строки ввода. Из-за FS = "" каждый символ в этой строке будет отдельным полем. Подписей нет NF Поля в записи: $1, $2, .., $(NF-1), и $NF. Предложение из трех частей if просто выводит внешние фигурные скобки и все, что не входит в фигурные скобки (то есть когда d == 0).

Можно расширить эту awk скриптлета, чтобы охватить комментарии, строки символов константы (используйте \047 для обозначения одной цитаты, если вы не поставить скрипт в отдельный файл с #!/usr/bin/awk -f), и обрабатывать или игнорировать препроцессор макросы ,

Это немного сложно, и вы получите несколько сотен строк awk-скрипта, но он должен быть достаточно надежным и достаточно быстрым. Причина, по которой это возможно, состоит в том, что правила токенизации в C в данном конкретном случае легко следовать; Я лично использовал бы полномасштабный C lexer (лексический анализатор или сканер) во всех других случаях использования. И, вероятно, для этого тоже.

Если вы хотите использовать полноэкранный C lexer, в сети есть множество доступных в сети, но вам нужно будет использовать язык более высокого уровня, такой как C или C++. Если вы хотите обрабатывать все угловые случаи, для этого потребуется также использовать препроцессор C/C++, но эти правила легки (даже с awk).

3

На последовательно отформатированный файл, вы может сделать что-то вроде

sed '/{$/ {:r;/\n}/!{N;br}; s/\n.*\n/\n/}' 

чтения тела функции сразу и удалить все между фигурными скобками:

$ echo 'function add1 (int i) { 
    if (i == 1) {return i+1;} 
}' | sed '/{$/ {:r;/\n}/!{N;br}; s/\n.*\n/\n/}' 
function add1 (int i) { 
} 

Команда работает только с блоками, начиная с { непосредственно перед и заканчивая } непосредственно после новой строки.

В :r;/\n}/!{N;br} части :r определяет label имени r, в котором другая линия прилагается к шаблону пространства от входа (N), а затем поток выполнения переходит к началу r снова (br). Это происходит только до тех пор, пока не встретится \n}. Поэтому, когда мы выходим из этого «цикла», у нас есть все тело функции в пространстве шаблонов, а затем мы применяем команду s.

+0

Спасибо за ответ. Хотя я понимаю большую часть 'sed', не могли бы вы объяснить, что' {: r; 'и'! {N; br}; 'do? – pap42

+0

@ pap42 Я добавил короткое объяснение, не стесняйтесь спросить, не упустил ли я что-нибудь важное. –

0

Я бы сначала предложил убедиться, что ваш исходный файл C имеет правильный отступ. Вы можете использовать для этого indent -gnu.

Тогда вы можете использовать некоторые sed трюки. При правильном отступом код вам нужно только заботиться о брекетах (открытие или закрытие) в качестве первого символа их линий.

Я не уверен, почему вы хотите это сделать. В частности, struct может быть, а иногда и вложенным. И есть патологические случаи - например. препроцессорные макросы, определяющие элементы с фигурными скобками и т. д.

Лучшим способом может быть работа с внутренними компонентами компилятора (но тогда вам нужно иметь дело с материалами, исходящими от #include-d заголовков). Для этой цели вы можете использовать MELT (MELT - это высокоуровневый доменный язык для расширения GCC и работает с внутренними компонентами GCC).

Смежные вопросы