おいふぉりーのぶろぐ

きっと趣味のブログに違いないです!!

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

正規表現で一括ソースコード変換

ちょっと去年あたりから書いている,MFC のネイティブコードで 5 万行程度のプログラムがあるんですが,書いているうちにいくつか修正したいところが出てきました.

1. bool 型を使った部分があるが,やはり MFC のコードでは BOOL に統一したい.
2. ユニコード専用のコードにしているが,一応汎用文字列 TCHAR 対応したい.

ちなみに,このプロジェクトのファイルの総数は 2, 3 百ほどあるので,さすがに手作業で直すのは泣きそうです. ということで,指定したフォルダ内のテキストファイルについて,正規表現の規則にしたがって一括で文字列を置換するプログラムを書いてみました.C++で!!スクリプト言語 (Perl とか Ruby とか) を使ったらいいんじゃないか?とか言っちゃいけません (私はスクリプト系はぜんぜん使ったことがないもので).Visual Studio 2008 Feature Pack 以降では,C++ でも TR1 の正規表現 (std::tr1::regex) ライブラリが標準で使えるので,複雑な文字列処理も結構簡単に書けます (たぶん).もちろん,boostregexを使ってもいいのですが.

それでは,置換プログラムは100行ちょいのコードなので,使い方と一緒に続きに置いておきます.ソースコードの変換以外にも使えるかも.


ソースコードはこんな感じ↓

リスト RegexSrcConv.cpp
#include <windows.h>
#include <iostream>
#include <regex>
#include <locale>
#include <vector>
#include <algorithm>
#include <fstream>
#include <sstream>

using namespace std;

void loadPattern(wstring const &fileName, vector<pair<wstring, wstring> > &pattern)
{
  wifstream file(fileName.c_str());
  wstring line;
  while (getline(file, line))
  {
    if (line.empty()) continue;
    if (line[0] == L'@'continue;
    size_t pos = line.find_first_of(L"=>");
    if (pos != wstring::npos)
    {
      pattern.push_back(make_pair(line.substr(0, pos), line.substr(pos + 2)));
    }
  }
}

wstring extractFileExt(wstring const &fileName)
{
  wstring ext;
  for (int i = fileName.size() - 1; i >= 0; --i)
  {
    if (fileName[i] == L'.'return fileName.substr(i + 1);
  }
  return L"";
}

void enumerateFileRecursive(wstring const &dirName, vector<wstring> const &exts, vector<wstring> &fileNames)
{
    WIN32_FIND_DATA fd;
    HANDLE h = ::FindFirstFileW((dirName + L"\\*").c_str(), &fd);
    if (h == INVALID_HANDLE_VALUE) return;
  for ( ; ; )
    {
      wstring fileName = fd.cFileName;
      if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
      {
        if (fileName != L"." && fileName != L"..")
        {
          enumerateFileRecursive(dirName + L"\\" + fileName, exts, fileNames);
        }
      }
      else
      {
        wstring ext = extractFileExt(fileName);
        if (find(exts.begin(), exts.end(), ext) != exts.end())
        {
          fileNames.push_back(dirName + L"\\" + fileName);
      }
    }
        if (!::FindNextFileW(h, &fd)) break;
    }
    ::FindClose(h);
}

void convertFile(wstring const &fileName, vector<pair<wstring, wstring> > const &pattern)
{
  wstringstream buf;
  {
    wifstream file(fileName.c_str());
    buf << file.rdbuf();
  }
  
  wstring str = buf.str();
  for (size_t i = 0; i < pattern.size(); ++i)
  {
    tr1::wregex r(pattern[i].first);
    str = tr1::regex_replace(str, r, pattern[i].second);
  }
  
  {
    wofstream file(fileName.c_str());
    file << str;
  }
}

int wmain(int argc, wchar_t *argv[])
{
  try
  {
    locale::global(locale("japanese"));
    
    if (argc < 4)
    {
      wcout << L"RegexSrcConv 正規表現定義ファイル ディレクトリ名 拡張子1 拡張子2 ..." << endl;
    }
    else
    {
      vector<pair<wstring, wstring> > pattern;
      loadPattern(argv[1], pattern);
      wcout << L"正規表現定義ファイルから " << pattern.size() << L" 個の項目を読み込みました." << std::endl;
      vector<wstring> exts;
      for (int i = 3; i < argc; ++i) exts.push_back(argv[i]);
      vector<wstring> fileNames;
      enumerateFileRecursive(argv[2], exts, fileNames);
      wcout << fileNames.size() << L" 個のファイルが見つかりました. 変換処理を開始します." << endl;
      for (size_t i = 0; i < fileNames.size(); ++i)
      {
        convertFile(fileNames[i], pattern);
        wcout << (i + 1) << L" / " << fileNames.size() << L": ファイル " << fileNames[i] << L" を変換しました." << endl;
      }
      wcout << L"すべての変換処理が完了しました." << endl;
    }
  }
  catch (exception &e)
  {
    cout << "Exception: " << e.what() << endl;
  }
  catch (...)
  {
    cout << "Unknown exception!" << endl;
  }
  
  return 0;
}

コンパイルしたら,次のような,正規表現の変換定義ファイルを用意します.@から始まる行はコメントです.1行に"正規表現=>置換フォーマット"という風に書きます.
正規表現に使える文法はライブラリ任せなので,ここを見てください.


リスト pattern.txt
@ ソースファイルを正規表現を用いてフォルダ内のファイルを一括で変更します.

@ ワイド文字列から TCHAR (汎用テキスト) への変更.
@ wchar_t const * → LPCTSTR
@ const wchar_t * → LPCTSTR
@ wchar_t * → LPTSTR
@ wchar_t → TCHAR
@ L"XXX" → _T("XXX")
wchar_t const \*=>LPCTSTR
const wchar_t \*=>LPCTSTR
wchar_t \*=>LPTSTR
wchar_t=>TCHAR
L"([^"]*)"=>_T("$1")

@ bool から BOOL への変更
bool=>BOOL
false=>FALSE
true=>TRUE

次のような,コンパイルして得られた実行ファイル RegexSrcConv.exe に対して次のように引数を与えて実行します.

RegexSrcConv.exe pattern.txt C:\xxx\yyy\zzz h cpp

指定したフォルダ C:\xxx\yyy\zzz から再帰的にサブフォルダを検索し,拡張子 h と cpp に一致するファイルに対して,置換処理を行います.
  1. 2010/05/16(日) 01:49:56|
  2. ソフトウェア開発
  3. | トラックバック:0
  4. | コメント:0
<<MFC で typeid 演算子を使うとメモリリークするそうです | ホーム | Visual C++ 2010 Express 導入>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://tm86eublog.blog42.fc2.com/tb.php/241-d8e9a42f
この記事にトラックバックする(FC2ブログユーザー)

来客数

プロフィール

Euphorie

Author:Euphorie
"おいふぉりー"って呼んでくださいな☆
ハードウェアとかソフトウェアとかの開発に興味があったり。。。
連絡先は上の画像。

最近の記事

最近のコメント

最近のトラックバック

月別アーカイブ

カテゴリー

ブロとも申請フォーム

この人とブロともになる

ブログ内検索

RSSフィード

リンク

このブログをリンクに追加する

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。