正則表達式不匹配特定格式的一種方式和sed的多表達式應用

寫這個的初衷是爲了完成目下我自己遇到的需求,需要把部分linux下工作的c代碼遷移到windows環境下,並以dll庫的形式提供。函數需要增加如下聲明

__declspec(dllexport) void func( void * arg)

/* __declspec(dllexport)  在後續我直接宏定義爲了類似  WIN_DECLARE 的格式*/

我便考慮以正則表達式的形式,自動匹配到函數段,並在函數名的前面增加聲明,但函數名可能存在很多種的形式,希望匹配到所有情況不太現實,我主要編寫正則表達式適配了以下兩種函數聲明和實現的情況。

/*函數聲明1*/
void func( void * arg);

/*函數聲明2*/
void func( void * arg1 , void * arg2 ,
               void * arg3);

/*函數實現1*/
void func( void * arg)
{
   ...
}

/*函數實現2*/
void func( void * arg1 , void * arg2 ,
               void * arg3)
{
   ...
}

針對上述函數聲明1的情況,可以用如下正則進行一個簡單的匹配,我使用sed命令

sed 's/^\w.*([^()]*);$/WIN_DECLARE &/g;'  test.c

從左到右,分四部分簡單說明一下這個表達式

  • ^\w 就是以任意字母,數字或下劃線爲行的開頭(此處忽略了開頭有空行的函數聲明,是對這種情況的犧牲),目的是爲了與 if () 語句的情況區分開
  • .*( 匹配從第二個字符開始到第一個左括號
  • [^()]*);$ 匹配到結尾的 ); 格式,並且從左括號,到結尾之間,包含任意個不存在 左右括號的字符(略過那種長表達式,但是同時也略過了那些帶強制類型轉換的函數聲明,但一般在函數聲明中加強制類型轉換好像也不太合理)
  • WIN_DECLARE & 是sed ‘s///g’ 表達式的第二部分即替換部分,用 & 表徵前面匹配到的完整字符串,然後再加上WIN_DECLARE前綴即可,這也是我自己定義的。

針對其他幾種情況,也列舉正則表達式如下,但是與如上這種均是大同小異。

/*for 聲明2*/
sed 's/^\w.*([^()]*,$/WIN_DECLARE &/g;'  test.c

/*for 實現1*/
sed 's/^\w.*([^()]*)$/WIN_DECLARE &/g;'  test.c

/*for 實現2*/
sed 's/^\w.*([^()]*,$/WIN_DECLARE &/g;'  test.c

用如上的表達式可以自動過濾出絕大部分的函數,並加上對應的聲明。
不過這裏我可能要考慮幾種更細緻的處理。

  1. 多次對同一文件使用該表達式,結果仍然是正確的。即使執行兩次也不能出現函數頭部變成如下的情況。
WIN_DECLARE WIN_DECLARE void func( void * arg)

這是爲了保持該表達式的自身的強壯性,因爲我可能最終會對一個文件夾中的所有文件都使用該命令,而使用過後,我可能又會往文件夾添加新的文件。故而需要保證已經調整過的文件仍能正常工作。

針對該需求,可以在每次執行表達式前,先把WIN_DECLARE清除掉,使用 sed 的多表達式功能來完成。sed命令可以使用 -e 來在一次匹配中,以從左到右的順序先後使用表達式來做過濾。具體如下

sed -e 's/^WIN_DECLARE[ \t]*//g' -e 's/^\w.*([^()]*);$/WIN_DECLARE &/g' test.c

上述表達式比較簡單,就是以 WIN_DECLARE 開頭,後面若干個 空格或者tab 均匹配,然後替換掉,完成一步預處理,然後再執行後面的正則匹配。

  1. 我希望部分格式可以做特殊處理(過濾),即雖然也是函數,但不添加上述前綴,比如函數頭部爲static時,我不希望增加上述前綴,表達式也需要滿足這種特殊情況。

我瞭解一下資料,發現sed的一種特殊表達式可以滿足需求,即取反的組合表達式,列舉如下:

sed -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g' test.c

因爲一般static的聲明只在c的函數實現中有,故而我這裏的例子是針對上面的函數實現1的。後半段與前面是一致的,主要在於如下的前半段

/.*static.*([^()]*)$/! 

這也是屬於一個表達匹配式中的一部分,然後是匹配到這一行的開頭部分帶有 static字段,static 前後均可以有任意數量的字符,同時後段匹配 我們對函數實現的 正則表達式匹配,即匹配 ([^()]*)$ ,我們就認爲這是static聲明的函數,而這種函數顯然是內部使用,不需要通過 dllexport的聲明暴露出來,故而在表達式的末尾使用 ‘!’ 來取非,意味着我們後面匹配的表達式的前提,是不匹配這前半段的表達式。

將上述的幾個流程綜合在一起,單獨針對函數實現1 這種情況,我們可以得到如下的表達式。

sed -e 's/^WIN_DECLARE[ \t]*//g' -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g' test.c

當然,我們也可以把函數實現1和 函數實現2這兩種情況,用sed的 -e 多表達式來實現。在處理上效率會更高。

sed -e 's/^WIN_DECLARE[ \t]*//g' -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g'  -e '/.*static.*([^()]*,$/! s/^\w.*([^()]*,$/WIN_DECLARE &/g'  test.c

這幾乎是我寫過的最長的正則表達式組合語句。完成之後我也想到,這種思路可能在其他地方也可以應用上。關鍵點就是我在標題中描述兩個點,一是多表達式應用,二是通過sed實現過率特定格式的辦法。故而我撰寫了這篇博客,也算完成一個任務,同時也是我第一次對正則表達式做這種相對複雜的應用,故而特此記錄一下。

備註:
實際對文件做修改,當然要需要使用 sed -i 選項,但是使用這個選項務必慎重,在此,爲避免出現錯誤,在舉例的表達式中均不使用 -i選項。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章