代码之家  ›  专栏  ›  技术社区  ›  DougN

C++的格式

  •  27
  • DougN  · 技术社区  · 16 年前

    寻找一个C++实现的函数,比如.NET的Strug.Frad。显然,有printf和它的品种,但我正在寻找一些定位的东西,如:

    string.format(“你好0。你是 1岁。感觉怎么样 { 1 }?姓名、年龄;

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

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

    谢谢)

    13 回复  |  直到 7 年前
        1
  •  30
  •   Deduplicator    10 年前
        2
  •  15
  •   shoosh    16 年前

    qt的qstring允许您这样做:

    QString("Hi there %1. You are %2 years old. How does it feel \
             to be %2?").arg(name).arg(age)
    
        3
  •  10
  •   Logan Capaldo    15 年前

    信不信由你,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;
     }
    
        4
  •  7
  •   KindDragon    11 年前

    你可以看看 FastFormat -图书馆。

        5
  •  3
  •   dcw    15 年前

    我想你可以用 FastFormat 作为

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

    几乎是相同的语法。

        6
  •  3
  •   DougN    15 年前

    上面有很多很好的建议,在大多数情况下都是有效的。在我的例子中,我最终希望从资源中加载字符串,并尽可能保持字符串资源接近.NET string.format,所以我滚动了自己的字符串。在查看了上面的一些实现的想法之后,得到的实现是非常短和容易的。

    有一个类字符串,在我的例子中它是从Microsoft的CString派生的,但它可以从任何字符串类派生。还有一个类StringArg——它的任务是获取任何参数类型并将其转换为字符串(即它模仿.NET中的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 on变量的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;
    }
    
        7
  •  1
  •   Joel Coehoorn    16 年前

    几个选项:

    • Boost格式库(已提到)
    • 细流
    • 传统printf/sprintf函数
    • 使用正则表达式或内置字符串函数的自定义实现

    在相关的注释中,您所说的完全不适合本地化。

        8
  •  1
  •   Rodrigo Strauss    16 年前

    碘流:

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

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

        9
  •  0
  •   Salman A    16 年前

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

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

        10
  •  0
  •   Serge Wautier    16 年前

    瞄准窗户? FormatMessage() 是你的朋友

        11
  •  0
  •   Mihai Nita    15 年前

    如果你是跨平台的,那么我会投票给Boost::Format,或者ICU。 如果您应该只支持Windows,那么formatmessage(如果您使用MFC,还可以使用方便的包装器cstring::formatmessage)

    可以在这里进行比较: http://www.mihai-nita.net/article.php?artID=20060430a

        12
  •  0
  •   GiM    11 年前

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

    • 不支持位置格式(所以我想这对你来说是一种不可取的做法)
    • 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

        13
  •  0
  •   vitaut    7 年前

    除了其他人建议的选项外,我还可以推荐 fmt library 它实现类似于 str.format 在蟒蛇和 String.Format 在C语言中。下面是一个例子:

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

    该库是完全类型安全的,并且 much faster 而不是Boost格式。

    免责声明:我是这个图书馆的作者。