MFC/CStringをの3バイトや4バイト文字を対応出来るように変更する方法はありますか

MFCプログラマー 101 評価のポイント
2024-04-16T00:09:12.52+00:00

開発環境は Windows11/ VisualC++(MFC)です。

すこし前に MFC/C++ でCStringにユニコードの3バイトや4バイト文字が入っている時の取り出し方について質問したのですが、CStringからGetAt/Midなどで文字を取り出している箇所は1000箇所以上あるため どう処理をしたら良いか悩んでいます。

今のところ CStringEx(CString)クラスを作り、GetLength/Mid/Left/Rrightを2バイト以上に対応して1000箇所に入れようとしていますが , CString自体を3バイト以上に対応出来るよう修正する方法がないか? と思ってきました。

ご質問は2つです。

  1. MFC/CStringを3バイトや4バイト文字を対応出来るように変更する方法はありますか?
  2. MFCを使った大プログラム(MFC/CStringを多量使用)で、今回のように2バイト文字以上に対応できるようにする一般的方法があれば教えてください。 (ガイドラインのようなあれば嬉しいです)

よろしくお願いします。

C++
C++
C プログラミング言語の拡張機能として作成された高レベルの汎用プログラミング言語。低レベルのメモリ操作機能に加えて、オブジェクト指向、汎用、関数型の機能を備えています。
11 件の質問
0 件のコメント コメントはありません
{count} 件の投票

承認済みの回答
  1. gekka 6,671 評価のポイント MVP
    2024-04-16T18:49:57.11+00:00

    >1

    CStringという文字列を#defineで別名に置換することで本来のCStringを置き換えてみました。

    // pch.h: プリコンパイル済みヘッダー ファイルです。
    // 次のファイルは、その後のビルドのビルド パフォーマンスを向上させるため 1 回だけコンパイルされます。
    // コード補完や多くのコード参照機能などの IntelliSense パフォーマンスにも影響します。
    // ただし、ここに一覧表示されているファイルは、ビルド間でいずれかが更新されると、すべてが再コンパイルされます。
    // 頻繁に更新するファイルをここに追加しないでください。追加すると、パフォーマンス上の利点がなくなります。
    
    #ifndef PCH_H
    #define PCH_H
    
    #define CString CStringDef
    
    // プリコンパイルするヘッダーをここに追加します
    #include "framework.h"
    
    #include <icu.h>
    #pragma comment(lib, "icu.lib")
    
    #undef CString
    
    template< typename BaseType, class StringTraits >
    class CStringExT : public ATL::CStringT<BaseType, StringTraits> {
    	using CStringT::CStringT;
    
    private:
    	int32_t GraphemeLength(UBreakIterator* pIterator)
    	{
    		LPCWSTR p = GetBuffer();
    
    		int32_t count = 0;
    		int32_t temp;
    		while (UBRK_DONE != (temp = ubrk_next(pIterator))) {
    
    			count++;
    			if (p[temp] == L'\0')
    			{//末尾を超えて列挙してしまうバグがある?
    				//break;
    			}
    		}
    		return count;
    	};
    public:
    	int32_t GraphemeLength()
    	{
    		CStringDef d;
    		d.GetBuffer();
    
    		LPCWSTR p = GetBuffer();
    
    		UErrorCode errcode;
    		UBreakIterator* pIterator = ubrk_open(UBRK_CHARACTER, ULOC_JAPAN, (UChar const*)p, -1, &errcode);
    
    
    		uint32_t count = GraphemeLength(pIterator);
    		return count;
    	};
    
    	CStringExT GraphemeMid(int32_t iFirst, int32_t nCount)
    	{
    		LPCWSTR p = GetBuffer();
    
    		UErrorCode errcode;
    		UBreakIterator* pIterator = ubrk_open(UBRK_CHARACTER, ULOC_JAPAN, (UChar const*)p, -1, &errcode);
    
    		ubrk_first(pIterator);
    		int32_t start = skipGrapheme(pIterator, iFirst, p);
    		int32_t end = skipGrapheme(pIterator, nCount, p);
    
    		ubrk_close(pIterator);
    		return CStringT::Mid(start, end - start);
    	};
    	CStringExT GraphemeMid(int32_t iFirst)
    	{
    		LPCWSTR p = GetBuffer();
    
    		UErrorCode errcode;
    		UBreakIterator* pIterator = ubrk_open(UBRK_CHARACTER, ULOC_JAPAN, (UChar const*)p, -1, &errcode);
    
    		ubrk_first(pIterator);
    		int32_t start = skipGrapheme(pIterator, iFirst, p);
    
    		ubrk_close(pIterator);
    		return CStringT::Mid(start);
    	};
    
    	CStringExT GraphemeLeft(int32_t count) {
    		LPCWSTR p = GetBuffer();
    
    		UErrorCode errcode;
    		UBreakIterator* pIterator = ubrk_open(UBRK_CHARACTER, ULOC_JAPAN, (UChar const*)p, -1, &errcode);
    
    		int32_t start = ubrk_first(pIterator);
    		int32_t end = skipGrapheme(pIterator, count, p);
    
    		ubrk_close(pIterator);
    		return CStringT::Mid(start, end - start);
    	};
    
    	CStringExT GraphemeRight(int32_t count)
    	{
    		CString ret;
    		LPCWSTR p = GetBuffer();
    
    		UErrorCode errcode;
    		UBreakIterator* pIterator = ubrk_open(UBRK_CHARACTER, ULOC_JAPAN, (UChar const*)p, -1, &errcode);
    
    		int index = ubrk_first(pIterator);
    		int len = GraphemeLength(pIterator);
    
    		int skipCount = len - count;
    		if (skipCount <= 0)
    		{
    			ret = CStringExT(p);
    		}
    		else
    		{
    			ubrk_first(pIterator);
    			int start = skipGrapheme(pIterator, skipCount, p);
    			ret = CStringExT(p + start);
    		}
    
    		ubrk_close(pIterator);
    		return ret;
    	};
    
    
    #if OVERRIDE_CSTRING_DEFAULT_METHODS
    	CStringExT Mid(int iFirst)
    	{
    		//return CStringDef::Mid(iFirst);
    		return GraphemeMid(iFirst);
    
    	}
    	CStringExT Mid(int iFirst, int nCount)
    	{
    		//return CStringDef::Mid(iFirst,nCount);
    		return GraphemeMid(iFirst, nCount);
    	}
    	CStringExT Left(int nCount)
    	{
    		//return CStringDef::Left(nCount);
    		return GraphemeLeft(nCount);
    	}
    	CStringExT Right(int nCount)
    	{
    		//return CStringDef::Right(nCount);
    		return GraphemeRight(nCount);
    	}
    #endif
    private:
    	static int32_t skipGrapheme(UBreakIterator* pIterator, int32_t count, LPCWSTR const p = nullptr)
    	{
    		uint32_t index = ubrk_current(pIterator);
    		while (count--)
    		{
    			int32_t end = ubrk_next(pIterator);
    			if (end == UBRK_DONE)
    			{
    				break;
    			}
    
    
    			index = end;
    
    			if (p != nullptr && p[end] == L'\0')
    			{//末尾を超えて列挙してしまうバグがある?
    				break;
    			}
    		}
    		return index;
    	};
    };
    
    typedef CStringExT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;
    #endif //PCH_H
    

    Mid,Left,Rightをどのような意図で使用しているのかは、実際に使用しているコードに依存するので、既存の関数を単純な置換で置き換えるのは止めた方がいいと思います。

    たとえばデータベースで固定長や文字数上限のあるの文字型の列に格納する文字列を用意する事を意図して使用している箇所で、書記素区切りの文字列を設定しようとしたら意図せず大きな文字列になってエラーになる可能性があります。

    1 人がこの回答が役に立ったと思いました。

1 件の追加の回答

並べ替え方法: 最も役に立つ
  1. とっちゃん 400 評価のポイント MVP
    2024-04-16T03:03:26.71+00:00

    1 は現実的な解としては、ないと思います。もし頑張るとしたら、MFCをプライベートビルド(自前ビルド版)にして、CStringT<T> を作り替えることになるのではないでしょうか。

    2 は一般的には以下の2パターンに分かれます。

    おそらく最初に取っているであろうと思いますが、一つ目は、置き換えたいメソッドに変わる関数を用意するという形。

    変更範囲が限定的(今回の事例で言えば、Left/Mid/Right程度でよい?)で、全体を作り替えるよりはいいという場合がこれに当たると思います。
    もう一つは新たにクラスを作成してそれに置き換えるという形。

    こちらは、文字列型のようなプリミティブに近い型ではあまり行わない事例だと思います。

    個人的には代替関数を作ってそれに置き換える形かなぁ?と思います(クラスを作って置き換えるより圧倒的に工数が少なくて済む)。