RTWEditorを完成させたので、一応ECCSkeltonライブラリーはVersion 1.0としては完成しました。では次は何をやろうか、というお話です。(プログラミング解説ではなく、ブログとしてお読みください。)

 

元々暇プロであった私(注)が、3年のブランクの後、ウィンドウズプログラミングに目覚めたのは(今はもう跡形もない)ActiveBasicという言語によってでしたが、すぐにフリーソフトのBCC5.5によるプログラミングに関心が移りました。

注:【昔話】私の8ビット時代【昔話】私の16ビット時代【昔話】私の32ビット時代

 

元々Cはカーニハンアンドリッチーで覚え、既に実用プログラム(といってもZ80アッセンブラーですが...汗)も書いていましたが、C++のクラス、継承、オーバーロード、抽象化とオーバーライド等、Cからの発展は大きく、一から出直しの学習でした。そんな中で、コード量の多いWin32SKDプログラミングに疑問を覚え、C++の学習をしながら効率的なライブラリーを使いたい、ということでBCCSkeltonを発展させてきたのですが、最初に書いたクラスは22年前にBCCFormを開発する際に書いたCSTRだったと記憶します。

 

BCCFormを作るうえで、C++の文字列処理の面倒くささから、「可変長の文字列を扱える変数(オブジェクト)で、文字列の結合、切り出しや比較演算等ができるもの」が必要であり、「ないなら作っちゃおう()」ということでCSTRクラスを書いた記憶があります。

:C++の標準ライブラリーにはstd::stringクラスというものがありますが、いつからライブラリーに入ったかは記録が無いのでわかりませんが、始まりはbasic_stringクラスといったようで、1990年代前半のユニコード標準化までは余り知られていなかったのかしら?少なくともBCCSkeltonを作っている時点では(私も初心者だったし)この存在を知りませんでした。今回std::stringの歴史を調べましたが、歴史的にその発展を記述しているサイトは見当たらず、結局BCCSkeltonが完成した2002年時点のstringクラスがどんなものであったかはわかりませんでした。当時CSTRを書かずにstringクラスを使っていたならばどうなっていただろう、と感慨深いです。なお、

 

注意:これから正しくC++を書こうと考えている方は、現在の標準ファイブラリーを基にstd::stringクラスを使ってください。(とはいえ、今からCSTRをstringに書き換えるだけでは何の意味もないので、私はCSTRを使い続けますが。汗;)

 

所でこのCSTRクラスは当時BCCFormを作るために必要な文字列処理をメンバー関数にした為、文字列処理ではあまり大したことをやっていませんが、stringクラスにはないファイル入出力が可能なために結構重宝しています。(また、デバッグでPrint()関数を使うことも多いです。)

 

所がCSTRにはstringクラスにある文字列の検索、置換処理の関数が基本的にありませんでした。(注)

注:公式発表ベースでは、という意味です。CSTR.hを見ると最後に、

    //文字列の検索
    char* CSTR::Find(char* ToFind) {

        return strstr(m_str, ToFind);
    }
という記述がありますが、この関数は何度実行してもCSTRのインスタンスの最初のToFindの位置を返すだけの「木偶の棒」関数です。(笑)
 

そんなこんなで、これではCSTRが可哀想、という想いで、(偶々CREDITクラスで文字列の検索・置換ダイアログを扱ったこともあり)このクラスに3つの関数を追加しました。(同時にECCSkeltonにも同様の関数を追加しています。)

 

//文字列の検索
char* CSTR::Find(char* ToFind, char* sp = 0) {

    if(!sp)
        sp = m_str;
    return strstr(sp, ToFind);
}


この関数は↑のいい加減な関数ではなく、CSTR::Find("探したい文字列");とすると最初の「探したい文字列の先頭アドレス」が返るので、次の検索にはCSTR::Find("探したい文字列", (第1回目の戻り値));とすることで検索を継続することができます。

//文字列の置換
char* CSTR::Replace(char* ToFind, char* ToReplace, char* sp = 0) {

    //CSTRのデータ長、検索文字長、置換文字長を計算する
    int OrgLen = lstrlen(m_str);
    int ToFindLen = lstrlen(ToFind);
    int ToReplaceLen = lstrlen(ToReplace);
    //spの指定が無ければCSTRデータの先頭から検索する
    if(!sp)
        sp = m_str;
    //検索文字列ポインターの取得
    char* Found_at = strstr(sp, ToFind);        //検索文字列が無ければNULLを返す
    //検索文字があれば
    if(Found_at) {
        //置換後の文字列データバッファーの確保
        char* buff = new char[OrgLen - ToFindLen + ToReplaceLen + 1];    //NULL終端を追加
        *Found_at = NULL;
        lstrcpy(buff, m_str);                    //検索文字列(ToFind)までをbuffにコピー
        lstrcat(buff, ToReplace);                //検索文字列(ToFind)の代わりに置換文字列をコピー
        lstrcat(buff, Found_at + ToFindLen);    //検索文字列(ToFind)以降の文字列をコピー
        buff[OrgLen - ToFindLen + ToReplaceLen] = NULL;    //NULL終端を付ける
        delete [] m_str;                        //CSTRデータ(m_str)を削除
        m_str = new char[OrgLen - ToFindLen + ToReplaceLen + 1];    //新しいデータ領域を確保
        lstrcpy(m_str, buff);                    //置換後のデータをコピー
        delete [] buff;                            //文字列データバッファーを破棄
    }
    return Found_at;    //置換できなかった場合NULL、できた場合置換文字列の先頭ポインター
}


このReplace関数はFind関数を使ってToFind文字列を探し、それをToReplace文字列に交換する処理を行います。引数の与え方はFind関数を同じ様にしているので、継続置換処理も可能です。

//文字列の全置換
void CSTR::ReplaceAll(char* ToFind, char* ToReplace, char* sp = 0) {

    char* Next = sp;
    while((Next = Replace(ToFind, ToReplace, Next)))
        Next += lstrlen(ToReplace);
}

 

最後はReplace関数を使った全置換関数で、Replace関数の繰り返し処理を行います。CSTR::ReplaceAll("検索文字列"、"置換文字列")でCSTRのはじめから全置換します。(途中で開始するには文字列ポインターspに値を与えることが必要です。)参考になるのではないでしょうか?

なお、ECCSkeltonではchar→WCHARの他、lstr***関数はlstr***W関数、strstr関数はStrStrW関数に置き換える必要があります。

 

所で、まだ本題(「次のブログネタ」)に入っていませんでした。

今回(ECCSkeltonの完成と共に)CSTRにこれらの関数を新設したので、これを使った久々のツールを考えています。名付けて"BCC2ECC"、SkeltonWizardで(プロジェクト名).rcから、(プロジェクト名).bdp、(プロジェクト名).h、(プロジェクト名)Proc.h、(プロジェクト名).cppファイルが生成された段階で適用する「BCCSkelttonコード」を「ECCSkelttonコード」へ変更するツールです。

お楽しみに!