文字列の検索

VC++ の FindText() で文字列を検索します。
Common Dialog は設定された情報を通知してくれるだけで、実際の検索はアプリケーション側で行わなければなりません。
自動的に検索する方法は Rich Edit で文字列を検索する を参照して下さい。

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

プロジェクトの設定

  1. 新規プロジェクトを作成して、メニューを設定して下さい。
    キャプションID
    FILE(&F) 親メニュー
    終了(&X) IDM_EXIT
    &Test! IDM_TEST
  2. MAX_FIND_LEN は検索する文字の最大長です。
    hwndFind は FindText() のハンドルです。
    FINDREPLACE は FindText() で検索する構造体の定義です。
    szFind[] は検索する文字列を格納する領域です。
    szBuf[] は文字列を検索するための領域です。
    uFindMessage には FindText() からのメッセージであることを識別する値を設定します。
    hEdit は EditControl のハンドルです。
    dwPos は現在検索中の位置です。
    szText[] は検索対象の TEXT です。適当な文字列を定義して下さい。
        #define     MAX_FIND_LEN  80
        HWND        hwndFind = NULL;
        FINDREPLACE fr;
        char        szFind[MAX_FIND_LEN+1];
        char        szBuf[1000];
        UINT        uFindMessage = 0;
        HWND        hEdit        = NULL;
        DWORD       dwPos        = 0;
        char        szText[1000] =
            "int short char long unsigned double INT long double int\r\n"
                    :
            "short int char LONG int short double int long CHAR int\r\n";
        
  3. CALLBACK 関数の先頭で FindText() からのメッセージを識別して処理します。
    msg == uFindMessage のときが FindText() からのメッセージです。
    lpfr に FINDREPLACE のポインタを格納します。
    Flags & FR_FINDNEXT のときに、次の文字列を検索します。
    dwPos に検索を開始する位置を設定します。
    EditControl から szBuf[] に TEXT を格納して strstr() で検索します。
    SendMessage(hEdit,WM_GETTEXT,sizeof(szBuf),(LPARAM)szBuf);
    lpFound = strstr(&szBuf[LOWORD(dwPos)],lpfr->lpstrFindWhat);
    文字列が見つかったときは EM_SETSEL で検索されたワードを選択状態に設定します。
        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);
                    SendMessage(hEdit,WM_GETTEXT,sizeof(szBuf),(LPARAM)szBuf);
                    // カーソル位置以降のテキスト内で、検索文字列を検索する。
                    lpFound = strstr(&szBuf[LOWORD(dwPos)],lpfr->lpstrFindWhat);
                    // 文字列が見つかった場合、文字列の選択を行う。
                    if (lpFound)
                    {   dwPos = lpFound-&szBuf[0];
                        SendMessage(hEdit,EM_SETSEL,dwPos,strlen(lpfr->lpstrFindWhat)+dwPos);
                    }
                    else    MessageBox(hWnd,"The string was not found.","Find",MB_OK | MB_ICONINFORMATION);
                }
                // ダイアログが終了している場合には、ハンドルをNULLにセットする。
                if (lpfr->Flags & FR_DIALOGTERM)    hwndFind = NULL;
            }
        
  4. WM_CREATE: の処理です。
    FINDREPLACE 構造体をクリアします。
    RegisterWindowMessage() で FindText() からのメッセージを識別する値を設定します。
    CreateWindow() で EditControl を生成して szText[] を表示します。
            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);
                SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)szText);
                break;
        
  5. メニューから IDM_TEST: が呼ばれたときの処理です。
    FINDREPLACE 構造体を設定して FindText() で Common Dialog を呼び出します。
    FR_HIDEMATCHCASE で「大文字と小文字を区別する」のチェックボックスを隠しています。
            case WM_COMMAND :
                switch(LOWORD(wParam))
                {   case IDM_TEST:
                        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;
        
  6. メッセージループでは、IsDialogMessage() で FindText() のメッセージを処理します。
        while (GetMessage(&msg,NULL,0,0))
        {   if (hwndFind && IsDialogMessage(hwndFind,&msg)) continue;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        

【演習】

  1. プログラムを完成させて下さい。
  2. FR_MATCHCASE に変更すると「大文字と小文字を区別する」チェックボックスが表示されます。
    if (lpfr->Flags & FR_MATCHCASE) のときは、大文字と小文字を区別して検索します。
  3. 大文字と小文字を区別しないで検索して下さい。
    大文字と小文字を扱う関数には次のようなものがあります。
    これらの関数をうまく使って下さい。
    strcmpi() 関数は、大文字と小文字を区別しません。
    stricmp() 関数, strnicmp() 関数は、比較を行う前に英字を小文字に変換します。
    toupper() 関数は、小文字を大文字に変換します。
    tolower() 関数は、大文字を小文字に変換します。

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