2012-09-23 21 views
2

我想從源文件中創建一個包含所有函數/枚舉/結構體/ etc名的文件。對於這一點,我在此刻試圖用sed來完成這樣的事情:使用sed刪除C/C++文件上的函數體

(原始文件)

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

(sed的輸出)

function add1 (int i) { 
} 

換句話說,我想要刪除函數正文的實際內容。我到目前爲止還沒有得到它的工作。有什麼建議麼?

編輯:我想是這樣的,沒有成功(現在我想只讓在函數體空行):

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

你應該解釋你爲什麼這麼問。 –

回答

1

相反的sed,你總是可以在每個字符域模式下使用awkFS=""):

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詞法分析器(詞法分析器或掃描儀)。也可能是爲了這個。

如果你想使用一個成熟的C詞法分析器,網絡上有一些可以自由使用的語言,但你必須使用更高級的語言,比如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};'嗎? – pap42

+0

@ pap42我已經添加了一個簡短的解釋,隨意問我是否遺漏了任何重要的東西。 –

0

我會首先建議確保您的C源文件正確縮進。你可以使用indent -gnu

然後你可以使用一些sed技巧。使用正確縮進的代碼,您只需要關心大括號(打開或關閉)作爲其行的第一個字符。

我不確定你爲什麼想這麼做。特別是,struct可以,有時甚至是嵌套的。還有病理情況 - 例如預處理器宏定義大括號等東西。

一個更好的方法可能是在編譯器內部進行操作(但是你必須處理來自#include-d頭的東西)。您可以使用MELT(MELT是擴展GCC的高級別領域特定語言,並且正在開發GCC內部)。