C++ 的 String.Format
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/462580/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
String.Format for C++
提问by DougN
Looking for an implementation for C++ of a function like .NET's String.Format. Obviously there is printf and it's varieties, but I'm looking for something that is positional as in:
正在寻找像 .NET 的 String.Format 这样的函数的 C++ 实现。显然有 printf 和它的品种,但我正在寻找像这样定位的东西:
String.Format("Hi there {0}. You are {1} years old. How does it feel to be {1}?", name, age);
String.Format("嗨,{0}。你已经 {1} 岁了。{1} 感觉如何?", name, age);
This is needed because we're going to try and make it easier to localize our app, and giving the translators {0} and {1} to position anywhere in the sentence is much easier than giving them a %s, %d, %d which must be positioned in that order in their translation.
这是必需的,因为我们将尝试更轻松地本地化我们的应用程序,并且让翻译人员 {0} 和 {1} 定位句子中的任何位置比给他们一个 %s, %d, % 容易得多d 必须在其翻译中按该顺序定位。
I suppose search and replace with variable inputs (va_start, va_end, etc) is what I'll end up building, but if there is already a solid solution, that would be preferrable.
我想搜索和替换变量输入(va_start、va_end 等)是我最终要构建的,但如果已经有一个可靠的解决方案,那将是可取的。
Thanks :)
谢谢 :)
采纳答案by DougN
Lots of good recommendations above that would work in most situations. In my case, I ultimately wanted to load strings from a resource, AND keep the string resources as close to .NET String.Format as I could, so I rolled my own. After looking at some of the implementations above for ideas, the resulting implementation was quite short and easy.
上面有很多很好的建议,可以在大多数情况下使用。就我而言,我最终希望从资源加载字符串,并尽可能使字符串资源接近 .NET String.Format,所以我自己开发了。在查看了上面的一些实现的想法之后,最终的实现非常简短和容易。
There is a class String, which in my case derives from Microsoft's CString, but it could derive from about any string class. There is also a class StringArg -- it's job is to take any parameter type and turn it into a string (ie it mimics ToString in .NET). If a new object needs to be ToString'd, you just add another constructor. The constructor allows a printf-style format specifier for non-default formatting.
有一个 String 类,在我的例子中它派生自 Microsoft 的 CString,但它可以派生自任何字符串类。还有一个 StringArg 类——它的工作是获取任何参数类型并将其转换为字符串(即它模仿 .NET 中的 ToString)。如果一个新对象需要被 ToString 处理,你只需添加另一个构造函数。构造函数允许使用 printf 样式的格式说明符进行非默认格式设置。
The String class then accepts a string table ID for the source string, a number of StringArg parameters, and finally an optional HINSTANCE (I use lots of DLLs, any of which could host the string table, so this allowed me to pass it in, or use a DLL-specific HINSTANCE by default).
然后,String 类接受源字符串的字符串表 ID、一些 StringArg 参数,最后是一个可选的 HINSTANCE(我使用了很多 DLL,其中任何一个都可以承载字符串表,因此这允许我将其传入,或默认使用特定于 DLL 的 HINSTANCE)。
Usage examples:
用法示例:
dlg.m_prompt = String(1417); //"Welcome to Stackoverflow!"
MessageBox(String(1532, m_username)); //"Hi {0}"
As it is, it only takes a string ID for an input, but it would be trivial to add an input string instead of a string ID:
事实上,它只需要一个字符串 ID 作为输入,但是添加一个输入字符串而不是字符串 ID 会很简单:
CString s = String.Format("Hi {0}, you are {1} years old in Hexidecimal", m_userName, StringArg(m_age, "%0X"));
Now for the StringArg class which does the equivalent of ToString on variables:
现在对于 StringArg 类,它对变量执行等效于 ToString 的操作:
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
For the String class, there are a bunch of member functions and constructors that take up to 10 arguments. These ultimately call CentralFormat which does the real work.
对于 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);
};
Finally, the implementation (hopefully it's OK to post this much on StackOverflow, although the bulk of it is very simple):
最后,实现(希望可以在 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'QString("Hi there %1. You are %2 years old. How does it feel \
to be %2?").arg(name).arg(age)
' != *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;
}
回答by Eddie Parker
Look at the boost format library.
回答by shoosh
QT's QString allows you do to this:
QT 的 QString 允许你这样做:
#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;
}
回答by Logan Capaldo
Believe it or not, printf and friends support positional arguments.
信不信由你,printf 和朋友们支持位置论证。
std::string result;
fastformat::fmt(result, "Hi there {0}. You are {1} years old. How does it feel to be {1}?", name, age);
回答by Christof Schardt
You might have a look at the FastFormat-library.
您可能会查看FastFormat 库。
回答by dcw
I think you can use FastFormat, as
我认为你可以使用FastFormat,因为
stringstream s;
string a;
s << "this is string a: " << a << endl;
which is almost the identical syntax.
这几乎是相同的语法。
回答by Joel Coehoorn
Several options:
几个选项:
- boost format library (already mentioned)
- stringstreams
- legacy printf/sprintf functions
- custom implementation using regular expressions or built-in string functions
- boost 格式库(已经提到)
- 字符串流
- 遗留的 printf/sprintf 函数
- 使用正则表达式或内置字符串函数的自定义实现
On a related note, what you're talking about is completely inadequate for localization.
与此相关的是,您所谈论的内容完全不适合本地化。
回答by Eclipse
If you're going to be writing your own, search and replace is probably not the best approach, since most search/replace methods only let you replace one at a time, and do a very poor job of allowing escpae characters (like if you want to include the literal string {0}
in your output.
如果您打算自己编写,搜索和替换可能不是最好的方法,因为大多数搜索/替换方法只允许您一次替换一个,并且在允许 escpae 字符方面做得很差(就像如果您想要{0}
在输出中包含文字字符串。
You're much better off writing your own finite-state machine to walk through the input string, generating an output string on the fly in one pass. This allows you to handle escape characters and more complicated output functions (like localized dates {0:dd\MM\yyyy}
for example). It will give you way more flexibility in addition to being faster than a search/replace or a regex approach.
您最好编写自己的有限状态机来遍历输入字符串,一次性生成输出字符串。这允许您处理转义字符和更复杂的输出函数(例如本地化日期{0:dd\MM\yyyy}
)。除了比搜索/替换或正则表达式方法更快之外,它还将为您提供更多的灵活性。
回答by Rodrigo Strauss
iostream:
iostream:
##代码##You can format like sprintf (google for "iostream format") and its in the C++ standard.
您可以像 sprintf(谷歌为“iostream 格式”)及其在 C++ 标准中的格式。
回答by Mihai Nita
If you should be cross-platform, then I would vote for boost::format, or maybe ICU. If you should only support Windows, then FormatMessage (or the convenient wrapper of that, CString::FormatMessage, if you use MFC)
如果你应该是跨平台的,那么我会投票支持 boost::format 或者 ICU。如果你应该只支持 Windows,那么 FormatMessage(或者它的方便包装器,CString::FormatMessage,如果你使用 MFC)
Can take a look here for a comparison: http://www.mihai-nita.net/article.php?artID=20060430a
可以看看这里进行比较:http: //www.mihai-nita.net/article.php?artID= 20060430a