''[[FrontPage]]''
* C++と正規表現(regex++) テストその3(最終) [#u0885174]
C/C++言語ソースファイルを想定し、正規表現を使って解析する。~
~
&color(#FF0000,){とりあえずC++正規表現シリーズはこれで凍結とします。};~
&color(#FF0000,){ここまでできれば、あとは自分で検索対象を追加実装するなり、出力まわりを変更したりはできるはずです。};
[[C++入門/正規表現1]]~
[[C++入門/正規表現2]]
** 制約 [#bc40674b]
MFC、.NET関数を使用しないこと。~
~
#include <string> //
#include <iostream> // for cout
#include <sstream> // for ostringstream
#include <boost/regex.hpp> // for regex++
#include <tchar.h> //
// # include "x.x.txt" // (空)宣言文のあらゆる可能性をテスト
#define MODE_ONE 1 // 解析モード1(include)
#define MODE_TWO 2 // 解析モード2(define)
#define MODE_THREE 3 // 解析モード3(const)
#define MODE_FOUR 4 // 解析モード4(予備)
#define MODE_FIVE 5 // 解析モード5(予備)
//# define s_s_s s _T("// comment") // (空)宣言文のあらゆる可能性をテスト
//
using namespace std; // 名前空間
using namespace boost; // 名前空間
//
#ifdef UNICODE // 強引に対応。単独プロジェクトならOK?
typedef wregex tregex; // ま、正直どうやったら良いのか分からない。
typedef wstring tstring; //
typedef wsmatch tsmatch; //
//typedef wstringstream tstringstream; //
//typedef wprintf tprintf; // * 出力に関しては自分で統一すべきか
#else //
typedef regex tregex; //
typedef string tstring; //
typedef smatch tsmatch; //
//typedef stringstream tstringstream; //
//typedef wprintf tprintf; // * 出力に関しては自分で統一すべきか
#endif //
//
typedef unsigned char u1b; //
typedef signed char s1b; //
//
s1b analizer( tstring de_file, u1b mode ); // プロトタイプ
s1b styleRemove( tstring* de_file ); // プロトタイプ
//
s1b main( void ) // 正規表現のテスト(インクルード宣言を探す)
{ //
const tstring file( // ソースファイルの内容を仮定
_T("#include <windows.h> // インクルード宣言\n") //
_T("#include \"me.h.h\" \n") //
_T("#include \"stuff.h\" \n") //
_T("#include \"a.hpp.cpp\" // #include \"ab.h\"の代替、とかなんとか\n") //
_T("/* #include \"mantarou.h\" うまく\n") //
_T("いくかな?\n*/") //
_T("// #include \"b.h\"\n") ); //
//
styleRemove( (tstring*)&file ); // コメントの削除 ** キャストがないとなぜエラー?
analizer( file, MODE_ONE ); // モード1で呼び出し()
//
getchar(); // 表示結果確認用
return 0; //
}
s1b analizer( // ソースファイル解析関数
tstring de_file, // IN: 対象文字列先頭アドレス
u1b mode ) // IN: 解析モード
{ //
try // 例外処理
{ // メモリ参照エラーの可能性あり?
tregex target; // 検索文字列
//
switch( mode ) // 解析モードで分岐
{ //
case MODE_ONE: // 解析モード1(include)
target = // 検索文字列のコンパイル
// _T("\n[[:blank:]]*") // シビアに判定する場合用(ただし1行目はヒットしない。)
_T("#[[:blank:]]*(include)") // "#"と空白を挟んだ"include"
_T("[[:blank:]]*L?\"") // 0以上の空白文字に続く'"'
_T("(.*?)") // ファイル名を最短マッチ
// _T("\\.[hc]p{0,2})") // 拡張子を指定する場合用
_T("\"" ); // ダブルクォーテーション右
break; //
//
case MODE_TWO: // 解析モード2(define)
break; //
//
case MODE_THREE: // 解析モード3(const)
break; //
//
case MODE_FOUR: // 解析モード4(予備)
break; //
//
case MODE_FIVE: // 解析モード5(予備)
break; //
//
default: // その他の場合
break; //
} //
//
tstring::const_iterator start; // イテレータの宣言
tstring::const_iterator end; // イテレータの宣言
//
start = de_file.begin(); // 開始イテレータの設定
end = de_file.end(); // 末尾イテレータの設定
//
tsmatch partial; // マッチ情報を格納する
match_flag_type flags = match_default; // flagの初期設定
//
while(regex_search(start, end, partial, target, flags)) // ループ
{ // マッチが見つかった場合
printf( "partial[0] = %s\n", // 画面表示(マルチバイト)
string( partial[0].first, partial[0].second).c_str() ); // 部分文字列[0](全体)
wprintf( L"partial[1] = %s\n", // 画面表示(ワイド文字列)
wstring( partial[1].first, partial[1].second).c_str() ); // 部分文字列[1]
wprintf( L"partial[2] = %s\n", // 画面表示(ワイド文字列)
wstring( partial[2].first, partial[2].second).c_str() ); // 部分文字列[2]
wprintf( L"partial[3] = %s\n", // 画面表示(ワイド文字列)
wstring( partial[3].first, partial[3].second).c_str() ); // 部分文字列[3]
wprintf( L"partial[4] = %s\n\n", // 画面表示(ワイド文字列)
wstring( partial[4].first, partial[4].second).c_str() ); // 部分文字列[4]
//
start = partial[0].second; // 開始イテレータの更新(次のマッチを探す)
//
flags |= match_prev_avail; // flagの更新
flags |= match_not_bob; //
} //
} //
catch( const exception& e ) // 例外発生時処理
{ //
printf("解析エラー\n"); // 画面表示
return -1; // 異常終了
} //
//
return 0; // 処理終了
}
s1b styleRemove( // コメント削除関数
tstring* de_file ) // OUT: 対象文字列先頭アドレス
{ //
try // 例外処理
{ // メモリ参照エラーの可能性あり?
printf("コメント削除前のサイズ = %d\n", de_file->size() ); // 動作確認用
//
tregex block( // 検索対象文字列のコンパイル(ブロックコメント)
_T("/\\*") // 開始点"/*"
_T(".*?") // 最短マッチ
_T("\\*/") ); // 終了点"*/"
//
tregex line( // 検索対象文字列のコンパイル(1行コメント)
_T("[[:blank:]]*") // 0以上のブランク
_T("//.*?\n") ); // 続く"//"からはじめての改行まで
//
*de_file = regex_replace( // ブロックコメント削除(""で置換)
*de_file, block, _T(""), match_default ); // *** ブロックコメントから先に消すこと!
//
*de_file = regex_replace( // 1行コメント削除(""で置換)
*de_file, line, _T(""), match_default ); // *** なぜなら、"/**///"の並びでバグるから
//
printf("コメント削除後のサイズ = %d\n\n", de_file->size() ); // 動作確認用
} //
catch( const exception& e ) // 例外発生時処理
{ //
printf("コメント削除エラー\n"); // 画面表示
return -1; // 異常終了
} //
//
return 0; // 正常終了
}
表示結果
コメント削除前のサイズ = 197
コメント削除後のサイズ = 88
partial[0] = #include "me.h.h"
partial[1] = inlude
partial[2] = me.h.h
partial[3] =
partial[4] =
partial[0] = #include "stuff.h"
partial[1] = inlude
partial[2] = stuff.h
partial[3] =
partial[4] =
partial[0] = #include "a.hpp.cpp"
partial[1] = inlude
partial[2] = a.hpp.cpp
partial[3] =
partial[4] =
*** 修正履歴 (その2からの分も含む) [#u5fb6e49]
boost、regexライブラリのビルドに成功。~
Unicodeへの対応は強引だが可能?(なにがおもしろいのかがいまいち分からない)
*** 課題 [#a8b2c7cf]
インクルードファイル名が"xxxx.h.h"のように、複数のピリオドを含んでいたら?(打開済)~
defineによる定数定義が、文字定数や文字列によって行われていたら?(打開不可能?)
#define CHAR_THREE '3' // 文字定数なら打開可能
#define COMMENT "// comment" // コメント削除対象になってしまう。
→ ここまではさすがに対応しなくても良さそうな気がする。~
~
今はcoutで画面表示に成功しているが、UNICODE対応(wsmatch使用)にすると、コンパイルエラーとなる。(打開済)~
むしろ、sub_matchが文字列として表示されていることが奇跡的?(打開済)~
string( partial[0].first, partial[0].second).c_str() ) // マッチ開始ポイントと終了ポイントを利用してstring型を作る。