web-dev-qa-db-ja.com

C ++のString.Format

.NETのString.Formatのような関数のC++の実装を探しています。明らかにprintfとその種類がありますが、私は次のような位置にあるものを探しています。

String.Format( "こんにちは{0}。あなたは{1}歳です。{1}の気分はどうですか?"、name、age);

これが必要なのは、アプリのローカライズを簡単に行えるようにするためです。翻訳者{0}と{1}を文の任意の場所に配置する方が、%s、%d、%を指定するよりもはるかに簡単です。 dは、翻訳時にこの順序で配置する必要があります。

検索して変数入力(va_start、va_endなど)に置き換えることが最終的に構築するものだと思いますが、すでに固溶体がある場合は、それが望ましいでしょう。

ありがとう:)

27
DougN

上記の多くの優れた推奨事項は、ほとんどの状況で機能します。私の場合、最終的にはリソースから文字列をロードし、文字列リソースをできるだけ.NET String.Formatに近づけたいと思ったので、自分でロールしました。上記の実装のいくつかをアイデアとして検討した後、結果として得られた実装は非常に短く簡単でした。

私の場合はMicrosoftのCStringから派生したクラスStringがありますが、ほぼすべての文字列クラスから派生できます。 StringArgクラスもあります-それは任意のパラメータタイプを取り、それを文字列に変換することです(つまり、.NETのToStringを模倣します)。新しいオブジェクトをToStringする必要がある場合は、別のコンストラクターを追加するだけです。コンストラクターでは、デフォルト以外のフォーマット用にprintfスタイルのフォーマット指定子を使用できます。

次に、Stringクラスは、ソース文字列の文字列テーブルID、いくつかのStringArgパラメーター、および最後にオプションのHINSTANCEを受け入れます(私は多くのDLLを使用し、そのいずれかが文字列テーブルをホストできるため、これを渡すことができました。または、デフォルトでDLL固有のHINSTANCEを使用します)。

使用例:

dlg.m_Prompt = String(1417); //"Welcome to Stackoverflow!"
MessageBox(String(1532, m_username)); //"Hi {0}"

現状では、入力には文字列IDのみが必要ですが、文字列IDの代わりに入力文字列を追加するのは簡単です。

CString s = String.Format("Hi {0}, you are {1} years old in Hexidecimal", m_userName, StringArg(m_age, "%0X"));

次に、変数に対してToStringと同等の処理を行うStringArgクラスの場合:

class StringArg
{
StringArg(); //not implemented
        StringArg(const StringArg&); //not implemented
        StringArg& operator=(const StringArg&); //not implemented

    public:
        StringArg(LPCWSTR val);
    StringArg(const CString& val);
    StringArg(int val, LPCWSTR formatSpec = NULL);
    StringArg(size_t val, LPCWSTR formatSpec = NULL);
    StringArg(Word val, LPCWSTR formatSpec = NULL);
    StringArg(DWORD val, LPCWSTR formatSpec = NULL);
    StringArg(__int64 val, LPCWSTR formatSpec = NULL);
    StringArg(double val, LPCWSTR formatSpec = NULL);
    CString ToString() const;
private:
    CString m_strVal;
};

extern HINSTANCE GetModuleHInst(); //every DLL implements this for getting it's own HINSTANCE -- scenarios with a single resource DLL wouldn't need this

Stringクラスの場合、最大10個の引数を取るメンバー関数とコンストラクターが多数あります。これらは最終的に、実際の作業を行うCentralFormatを呼び出します。

class String : public CString
{
public:
    String() { }
    String(Word stringTableID, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, hInst); }
    String(Word stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, hInst); }
    String(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, hInst); }


    CString& Format(Word stringTableID, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst());
    CString& Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst());
private:
    void CentralFormat(Word stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst);
};

最後に、実装(大部分は非常に単純ですが、StackOverflowにこれだけ投稿しても問題ありません):

StringArg::StringArg(LPCWSTR val)
{
    m_strVal = val;
}

StringArg::StringArg(const CString& val)
{
    m_strVal = (LPCWSTR)val;
}

StringArg::StringArg(int val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%d"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(size_t val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(Word val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(DWORD val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(__int64 val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%I64d"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(double val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%f"; //GLOK
    m_strVal.Format(formatSpec, val);
}

CString StringArg::ToString() const
{ 
    return m_strVal; 
}


void String::CentralFormat(Word stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst)
{
    size_t argsCount = args.size();
    _ASSERT(argsCount < 10); //code below assumes a single character position indicator

    CString tmp;
    HINSTANCE hOld = AfxGetResourceHandle();
    AfxSetResourceHandle(hInst);
    BOOL b = tmp.LoadString(stringTableID);
    AfxSetResourceHandle(hOld);
    if(FALSE == b)
    {
#ifdef _DEBUG

        //missing string resource, or more likely a bad stringID was used -- tell someone!!
    CString s;
        s.Format(L"StringID %d could not be found!  %s", stringTableID, hInst == ghCommonHInst ? L"CommonHInst was passed in" : L"CommonHInst was NOT passed in"); //GLOK
        ::MessageBeep(MB_ICONHAND);
        ::MessageBeep(MB_ICONEXCLAMATION);
        ::MessageBeep(MB_ICONHAND);
        _ASSERT(0);
        ::MessageBox(NULL, s, L"DEBUG Error - Inform Development", MB_ICONSTOP | MB_OK | MB_SERVICE_NOTIFICATION); //GLOK
        }
#endif //_DEBUG

    CString::Format(L"(???+%d)", stringTableID); //GLOK
        return;
    }

    //check for the degenerate case
    if(0 == argsCount)
    {
        CString::operator=(tmp);
        return;
    }

    GetBuffer(tmp.GetLength() * 3); //pre-allocate space
    ReleaseBuffer(0);
    LPCWSTR pStr = tmp;
    while(L'\0' != *pStr)
    {
        bool bSkip = false;

        if(L'{' == *pStr)
        {
            //is this an incoming string position?
            //we only support 10 args, so the next char must be a number
            if(wcschr(L"0123456789", *(pStr + 1))) //GLOK
            {
                if(L'}' == *(pStr + 2)) //and closing brace?
                {
                    bSkip = true;

                    //this is a replacement
                    size_t index = *(pStr + 1) - L'0';
                    _ASSERT(index < argsCount);
                    _ASSERT(index >= 0);
                    if((index >= 0) && (index < argsCount))
                        CString::operator+=(args[index]->ToString());
                    else
                    {
//bad positional index

                        CString msg;
                        msg.Format(L"(??-%d)", index); //GLOK
                        CString::operator+=(msg);
                    }
                    pStr += 2; //get past the two extra characters that we skipped ahead and peeked at
                }
            }
        }

        if(false == bSkip)
            CString::operator+=(*pStr);
        pStr++;
    }
}


CString& String::Format(Word stringTableID, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    args.Push_back(&arg5);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    args.Push_back(&arg5);
    args.Push_back(&arg6);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    args.Push_back(&arg5);
    args.Push_back(&arg6);
    args.Push_back(&arg7);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    args.Push_back(&arg5);
    args.Push_back(&arg6);
    args.Push_back(&arg7);
    args.Push_back(&arg8);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(Word stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.Push_back(&arg1);
    args.Push_back(&arg2);
    args.Push_back(&arg3);
    args.Push_back(&arg4);
    args.Push_back(&arg5);
    args.Push_back(&arg6);
    args.Push_back(&arg7);
    args.Push_back(&arg8);
    args.Push_back(&arg9);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}
3
DougN
30
Eddie Parker

QTのQStringを使用すると、次のことが可能になります。

QString("Hi there %1. You are %2 years old. How does it feel \
         to be %2?").arg(name).arg(age)
15
shoosh

信じられないかもしれませんが、printfとその仲間たちは位置引数をサポートしています。

 #include <stdio.h>

 int main() {
   char *name = "Logan";
   int age = 25;
   printf("Hi there %1$s, you are %2$d years old. How does it feel to be %2$d?\n", name, age);
  return 0;
 }
10
Logan Capaldo

FastFormat -libraryをご覧ください。

7

FastFormat を使用できると思います。

std::string result;

fastformat::fmt(result, "Hi there {0}. You are {1} years old. How does it feel to be {1}?", name, age);

これはほとんど同じ構文です。

3
dcw

いくつかのオプション:

  • boostフォーマットライブラリ(すでに言及)
  • stringstreams
  • 従来のprintf/sprintf関数
  • 正規表現または組み込みの文字列関数を使用したカスタム実装

関連するメモとして、あなたが話していることは、ローカリゼーションには完全に不十分です。

1
Joel Coehoorn

iostream:

stringstream s;
string a;
s << "this is string a: " << a << endl;

Sprintf(「iostreamformat」のグーグル)のようにフォーマットでき、C++標準でフォーマットできます。

1
Rodrigo Strauss

Windowsをターゲットにしていますか? FormatMessage() はあなたの友達です

0
Serge Wautier

独自に作成する場合、ほとんどの検索/置換方法では一度に1つずつしか置換できず、escpae文字を許可するという非常に貧弱な仕事をするため、検索と置換はおそらく最善のアプローチではありません(リテラル文字列{0}を出力に含めたい。

独自の有限状態マシンを作成して入力文字列をウォークスルーし、1回のパスでその場で出力文字列を生成する方がはるかに優れています。これにより、エスケープ文字やより複雑な出力関数(たとえば、ローカライズされた日付{0:dd\MM\yyyy}など)を処理できます。これにより、検索/置換や正規表現のアプローチよりも高速であることに加えて、より多くの柔軟性が得られます。

0
Eclipse

クロスプラットフォームである必要がある場合は、boost :: format、または多分ICUに投票します。 Windowsのみをサポートする必要がある場合は、FormatMessage(または、MFCを使用している場合はその便利なラッパーであるCString :: FormatMessage)

比較のためにここを見てください: http://www.mihai-nita.net/article.php?artID=20060430a

0
Mihai Nita

他の人が提案したオプションに加えて、 fmt library をお勧めします。これは str.format in Python and String.Format C#で。次に例を示します。

string result = fmt::format("Hi {0}. You are {1} years old.", name, age);

ライブラリは完全にタイプセーフであり、Boost Formatよりも はるかに高速 です。

免責事項:私はこのライブラリの作成者です。

0
vitaut

少し前に、私はそのようなことをしようとしていましたが、いくつかの追加の仮定がありました:

  • 位置フォーマットはサポートされていません(したがって、それはあなたにとってはダメだと思います)
  • c ++ 2k3(いくつかの古いコードに組み込むことができるようにするため)
  • (ほとんど)依存関係なし(crtでさえ、sprintf依存関係なし)

私はそれに失敗しました、そしてそれは完全に未完成です、しかしあなたはまだいくつかの結果を見ることができます:

http://code.google.com/p/pileofcrap/source/browse/tests_format.cpp

http://code.google.com/p/pileofcrap/source/browse/format/Format.h

0
GiM