28

正在寻找 .NET 的 String.Format 等函数的 C++ 实现。显然有 printf 和它的品种,但我正在寻找一些位置如下的东西:

String.Format("您好,{0}。您已经 {1} 岁。成为 {1} 的感觉如何?", name, age);

这是必要的,因为我们将尝试使本地化我们的应用程序变得更容易,并且让翻译者 {0} 和 {1} 定位在句子中的任何位置比给他们一个 %s、%d、% 容易得多d 在翻译中必须按该顺序定位。

我想用变量输入(va_start、va_end 等)搜索和替换是我最终要构建的,但如果已经有一个可靠的解决方案,那将是可取的。

谢谢 :)

4

13 回答 13

30

查看boost 格式库。

于 2009-01-20T18:54:13.917 回答
15

QT 的 QString 允许你这样做:

QString("Hi there %1. You are %2 years old. How does it feel \
         to be %2?").arg(name).arg(age)
于 2009-01-20T19:20:22.583 回答
10

信不信由你,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;
 }
于 2009-03-08T18:38:12.933 回答
7

你可以看看FastFormat -library。

于 2009-01-21T09:36:56.287 回答
3

我认为您可以使用FastFormat,如

std::string result;

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

这几乎是相同的语法。

于 2009-03-16T09:05:34.987 回答
3

上面的许多好的建议在大多数情况下都可以使用。就我而言,我最终想从资源中加载字符串,并尽可能地使字符串资源接近 .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;
}
于 2009-03-26T13:52:38.350 回答
1

几个选项:

  • boost 格式库(已经提到过)
  • 串流
  • 旧版 printf/sprintf 函数
  • 使用正则表达式或内置字符串函数的自定义实现

在相关的说明中,您所说的内容完全不足以进行本地化。

于 2009-01-20T19:23:51.157 回答
1

如果您要自己编写,搜索和替换可能不是最好的方法,因为大多数搜索/替换方法只允许您一次替换一个,并且在允许 escpae 字符方面做得很差(例如,如果您想要{0}在输出中包含文字字符串。

你最好编写自己的有限状态机来遍历输入字符串,一次生成一个输出字符串。这允许您处理转义字符和更复杂的输出函数(例如本地化日期{0:dd\MM\yyyy})。除了比搜索/替换或正则表达式方法更快之外,它还将为您提供更大的灵活性。

于 2009-01-20T19:24:13.420 回答
0

iostream:

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

您可以像 sprintf (谷歌为“iostream 格式”)和它在 C++ 标准中的格式。

于 2009-01-20T20:53:03.380 回答
0

瞄准Windows?FormatMessage()是你的朋友

于 2009-01-22T21:04:54.087 回答
0

如果您应该跨平台,那么我会投票支持 boost::format,或者可能是 ICU。如果你应该只支持 Windows,那么 FormatMessage (或方便的包装,CString::FormatMessage,如果你使用 MFC)

可以看看这里做个对比:http ://www.mihai-nita.net/article.php?artID=20060430a

于 2009-11-10T10:58:02.477 回答
0

除了其他人建议的选项外,我还可以推荐fmt 库,它实现了类似于str.formatPython 和String.FormatC# 中的字符串格式。这是一个例子:

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

该库是完全类型安全的,并且比 Boost 格式快得多。

免责声明:我是这个库的作者。

于 2012-12-26T01:20:01.267 回答
0

前段时间,我试图做类似的事情,但有一些额外的假设:

  • 不支持位置格式(所以我想这不适合你)
  • 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

于 2013-04-01T16:47:31.470 回答