C ++のString.Format
-
19-08-2019 - |
質問
.NETのString.Formatなどの関数のC ++の実装を探しています。明らかにprintfがあり、それはさまざまですが、私は以下のように定位置にあるものを探しています:
String.Format(<!> quot;こんにちは{0}。あなたは {1}歳。気分はどうですか {1}?<!> quot ;、名前、年齢);
これは、アプリのローカライズを簡単にしようとするため、必要です。翻訳者{0}と{1}に文の任意の場所に配置するのは、%s、% d、%dは、翻訳でこの順序で配置する必要があります。
検索と変数入力での置換(va_start、va_endなど)が最終的に構築されると思いますが、既に堅実なソリューションがあれば、それが望ましいでしょう。
ありがとう:)
解決 6
上記の多くの推奨事項は、ほとんどの状況で機能します。私の場合、最終的にはリソースから文字列をロードし、文字列リソースをできる限り.NET String.Formatに近づけることを望んでいたので、自分でロールバックしました。上記の実装のいくつかを考えてみた結果、実装は非常に短く簡単になりました。
クラスStringがあります。これは、私の場合、MicrosoftのCStringから派生していますが、ほぼすべての文字列クラスから派生できます。クラス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;
}
他のヒント
ブースト形式ライブラリをご覧ください。
QTのQStringを使用すると、これを実行できます。
QString("Hi there %1. You are %2 years old. How does it feel \
to be %2?").arg(name).arg(age)
信じられないかもしれませんが、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;
}
FastFormat -libraryをご覧ください。
FastFormat を次のように使用できると思います
std::string result;
fastformat::fmt(result, "Hi there {0}. You are {1} years old. How does it feel to be {1}?", name, age);
これはほぼ同じ構文です。
いくつかのオプション:
- ブーストフォーマットライブラリ(既に言及)
- stringstreams
- 従来のprintf / sprintf関数
- 正規表現または組み込みの文字列関数を使用したカスタム実装
関連するメモでは、あなたが話していることはローカライズにはまったく不十分です。
iostream:
stringstream s;
string a;
s << "this is string a: " << a << endl;
sprintf(<!> quot; iostream format <!> quot;のgoogle)およびC ++標準でフォーマットできます。
自分で作成する場合、ほとんどの検索/置換方法では一度に1つずつしか置換できず、escpae文字を許可するという非常に悪い仕事をするため、検索と置換はおそらく最良のアプローチではありません(出力にリテラル文字列{0}
を含めたい場合など。
入力文字列をウォークスルーして、1回のパスで出力文字列をその場で生成するために、独自の有限状態マシンを記述する方がはるかに優れています。これにより、エスケープ文字やより複雑な出力関数(ローカライズされた日付{0:dd\MM\yyyy}
など)を処理できます。検索/置換または正規表現のアプローチよりも高速であることに加えて、より柔軟になります。
Windowsをターゲットにしていますか? FormatMessage()はあなたの友人です
クロスプラットフォームにする必要がある場合は、boost :: formatまたはICUに投票します。 Windowsのみをサポートする必要がある場合は、FormatMessage(またはMFCを使用する場合はCString :: FormatMessageの便利なラッパー)
比較のためにこちらをご覧ください: http:// www。 mihai-nita.net/article.php?artID=20060430a
しばらく前に、私はそのようなことをしようとしていましたが、いくつかの追加の前提条件がありました:
- 位置の書式設定はサポートされていません(だから、私はそれはあなたのために行ってはいけないと思います)
- 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
他の人によって提案されたオプションに加えて、 fmtライブラリをお勧めします。 Pythonでは str.format
、C#ではString.Format
。次に例を示します。
string result = fmt::format("Hi {0}. You are {1} years old.", name, age);
ライブラリは完全にタイプセーフで、 Boost Formatよりもはるかに高速です。
免責事項:私はこのライブラリの著者です。