文字列の検索と置き換え

VC++ の ReplaceText() で文字列を検索して置き換えます。
実際の検索と置き換えは、アプリケーション側で行わなければなりません。
自動的に検索と置き換えをする方法は Rich Edit で文字列を置き換える を参照して下さい。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの設定

  1. 新規プロジェクトを作成して、メニューを設定して下さい。
    キャプションID
    FILE(&F) 親メニュー
    終了(&X) IDM_EXIT
    キャプションID
    Edit(&E) 親メニュー
    Find...(&F) IDM_FIND
    Replace...(&R) IDM_REPLACE
    先頭行(&T) IDM_TOP
  2. MAX_FIND_LEN は検索/置き換えする文字の最大長です。
    hwndFind は FindText() のハンドルです。
    FINDREPLACE は FindText() で検索する構造体の定義です。
    szFind[] は検索する文字列を格納する領域です。
    szReplace[] は置き換える文字列を格納する領域です。
    szBuf[] は文字列を検索/置き換えするための領域で、szWrk[] はその作業領域です。
    uFindMessage には Common Dialog からのメッセージであることを識別する値を設定します。
    hEdit は EditControl のハンドルです。
    dwPos は現在検索中の位置です。
        #define     MAX_FIND_LEN  80
        HWND        hwndFind = NULL;
        FINDREPLACE fr;
        char        szFind[MAX_FIND_LEN+1];
        char        szReplace[MAX_FIND_LEN+1];
        char        szBuf[30001];           //Edit Buffer
        char        szWrk[30001];           //Work Area
        UINT        uFindMessage = 0;
        HWND        hEdit        = NULL;
        DWORD       dwPos        = 0;
        
  3. CALLBACK 関数の先頭で Common Dialog からのメッセージを識別して処理します。
    msg == uFindMessage のときが Common Dialog からのメッセージです。
    lpfr に FINDREPLACE のポインタを格納します。
        LRESULT  CALLBACK  WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
        {
            // [検索]コモンダイアログメッセージを処理する。
            if (msg==uFindMessage)
            {   LPFINDREPLACE lpfr = (LPFINDREPLACE)lParam;
                // [次を検索]コマンドを処理する。
                if (lpfr->Flags & FR_FINDNEXT)
                {   LPTSTR lpFound;
                    DWORD  dwCurPos = SendMessage(hEdit,EM_GETSEL,0,0);
                    // 位置が以前と同じ場合には、次の文字列を検索するよう位置を増分する。
                    if (LOWORD(dwCurPos)==dwPos) dwPos++;
                    else    dwPos = LOWORD(dwCurPos);
                    lpFound = strstr(&szBuf[LOWORD(dwPos)],szFind);
                    if (lpFound)    dwPos = lpFound-&szBuf[0];
                    else    MessageBox(hWnd,"The string was not found.","Find",MB_OK | MB_ICONINFORMATION);
                }
                // [置換]コマンドを処理する。
                if (lpfr->Flags & FR_REPLACE)
                {   strcpy(szWrk,szBuf+dwPos);
                    szBuf[dwPos] = 0;
                    strcat(szBuf,lpfr->lpstrReplaceWith);
                    strcat(szBuf,&szWrk[strlen(lpfr->lpstrFindWhat)]);
                }
                // [すべて置換]コマンドを処理する。
                if (lpfr->Flags & FR_REPLACEALL)
                    MessageBox(hWnd,"Replace All is not implemented.","Replace",MB_OK | MB_ICONINFORMATION);
                // 検索文字列を選択状態にする
                SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)szBuf);
                if (lpfr->Flags & FR_FINDNEXT)
                    SendMessage(hEdit,EM_SETSEL,dwPos,strlen(lpfr->lpstrFindWhat)+dwPos);
                if (lpfr->Flags & FR_REPLACE)
                    SendMessage(hEdit,EM_SETSEL,dwPos,strlen(lpfr->lpstrReplaceWith)+dwPos);
    
                // Edit Control を Scroll して、見えるようにする
                DWORD  pos= LineNO(dwPos);
                if (pos>10) SendMessage(hEdit,EM_LINESCROLL,0,pos-10);
    
                // ダイアログが終了している場合には、ハンドルをNULLにセットする。
                if (lpfr->Flags & FR_DIALOGTERM)    hwndFind = NULL;
            }
        
  4. WM_CREATE: の処理です。
    FINDREPLACE 構造体をクリアします。
    RegisterWindowMessage() で FindText() からのメッセージを識別する値を設定します。
    CreateWindow() で EditControl を生成して TEXT データを表示します。
    表示するデータは ReadData() でファイルから入力して szBuf[] に格納しています。
            case WM_CREATE:
                memset(&fr,0,sizeof(FINDREPLACE));
                uFindMessage = RegisterWindowMessage("commdlg_FindReplace");
                hEdit = CreateWindow("EDIT","",
                        WS_CHILD | ES_LEFT | WS_VISIBLE | ES_MULTILINE | ES_NOHIDESEL,
                        0,0,0,0,hWnd,(HMENU)101,g_hInst,NULL);
                ReadData("DATA.TXT",szBuf,30000);
                SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)szBuf);
                break;
        
  5. メニューから IDM_FIND: 及び IDM_REPLACE: が呼ばれたときの処理です。
    FINDREPLACE 構造体を設定して FindText() または ReplaceText() で Common Dialog を呼び出します。
    ReplaceText() は検索と置き換えを行う Common Dialog を呼び出す関数で、FindText() は検索を行う Common Dialog を呼び出す関数です。
    IDM_TOP: では EditControl に TEXT を設定しなおして、先頭行に位置づけています。
            case WM_COMMAND :
                switch(LOWORD(wParam))
                {   case IDM_FIND:
                        fr.lStructSize   = sizeof( FINDREPLACE );
                        fr.hwndOwner     = hWnd;
                        fr.lpstrFindWhat = szFind;
                        fr.wFindWhatLen  = MAX_PATH;
                        fr.Flags         = FR_HIDEMATCHCASE | FR_HIDEUPDOWN | FR_HIDEWHOLEWORD;
                        hwndFind = FindText(&fr);
                        break;
                    case IDM_REPLACE:
                        fr.lStructSize      = sizeof( FINDREPLACE );
                        fr.hwndOwner        = hWnd;
                        fr.lpstrFindWhat    = szFind;
                        fr.lpstrReplaceWith = szReplace;
                        fr.wFindWhatLen     = MAX_FIND_LEN;
                        fr.wReplaceWithLen  = MAX_FIND_LEN;
                        fr.Flags            = FR_MATCHCASE | FR_HIDEUPDOWN | FR_HIDEWHOLEWORD;
                        hwndFind = ReplaceText(&fr);
                        break;
                    case IDM_TOP:
                        SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)szBuf);
                        break;
        
  6. メッセージループでは、IsDialogMessage() で FindText() のメッセージを処理します。
        while (GetMessage(&msg,NULL,0,0))
        {   if (hwndFind && IsDialogMessage(hwndFind,&msg)) continue;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        

【演習】

  1. ReadData() 関数と LineNO() 関数を作成して、プログラムを完成させて下さい。
  2. [すべて置換] コマンドを処理する部分を作成して下さい。
  3. 大文字と小文字を区別しないで検索して下さい。
    大文字と小文字を扱う関数には次のようなものがあります。
    これらの関数をうまく使って下さい。
    strcmpi() 関数は、大文字と小文字を区別しません。
    stricmp() 関数, strnicmp() 関数は、比較を行う前に英字を小文字に変換します。
    toupper() 関数は、小文字を大文字に変換します。
    tolower() 関数は、大文字を小文字に変換します。

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

超初心者のプログラム入門(Win32API C++)