简体   繁体   English

使用包含%1,%2等的格式字符串,而不是%d,%s等。 - Linux,C ++

[英]Use format strings that contain %1, %2 etc. instead of %d, %s etc. - Linux, C++

As a follow-up of this question (Message compiler replacement in Linux gcc), I have the following problem: 作为这个问题的后续(Linux gcc中的消息编译器替换),我有以下问题:

When using MC.exe on Windows for compiling and generating messages, within the C++ code I call FormatMessage, which retrieves the message and uses the va_list *Arguments parameter to send the varied message arguments. 在Windows上使用MC.exe编译和生成消息时,在C ++代码中我调用FormatMessage,它检索消息并使用va_list *Arguments参数发送不同的消息参数。 For example: 例如:
messages.mc file: messages.mc文件:

MessageId=1
Severity=Error
SymbolicName=MULTIPLE_MESSAGE_OCCURED
Language=English
message %1 occured %2 times.
.

C++ code: C ++代码:

void GetMsg(unsigned int errCode, wstring& message,unsigned int paramNumber, ...)
{   
    HLOCAL msg;
    DWORD ret;
    LANGID lang = GetUserDefaultLangID();
    try
    {
        va_list argList;
        va_start( argList, paramNumber );
        const TCHAR* dll = L"MyDll.dll";
        _hModule = GetModuleHandle(dll);
        ret =::FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_IGNORE_INSERTS,
            _hModule,
            errCode,
            lang,
            (LPTSTR) &msg,
            0,
            &argList );
        if ( 0 != ret )
        {
            unsigned int count = 0 ;
            message = msg;
            if (paramNumber>0)
            {
                wstring::const_iterator iter;
                for (iter = message.begin();iter!=message.end();iter++)
                {
                    wchar_t xx = *iter;
                    if (xx ==L'%')
                        count++;
                }
            }
            if ((count == paramNumber) && (count >0))
            {

                ::LocalFree( msg );
                ret =::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER |
                    FORMAT_MESSAGE_FROM_HMODULE,
                    _hModule,
                    errCode,
                    GetUserDefaultLangID(),
                    (LPTSTR) &msg,
                    0,
                    &argList );
            }
            else if (count != paramNumber)
            {
                wstringstream tmp;
                wstring messNumber;
                tmp << (errCode & 0xFFFF);
                tmp >> messNumber;
                message = message +L"("+ messNumber + L"). Bad Format String. ";
            }
        }
        ::LocalFree( msg );
    }
    catch (...)
    {
        message << L"last error: " << GetLastError();
    }
    va_end( argList );
}

Caller code: 来电代码:

wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);

Now, I wrote a simple script to generate a .msg file from the .mc file, and then I use gencat to generate a catalog from it. 现在,我编写了一个简单的脚本来从.mc文件生成.msg文件,然后我使用gencat从中生成目录。

But is there a way to use the formatted strings as they contain %1, %2, etc. and NOT the general (%d, %s...) format? 但有没有办法使用格式化的字符串,因为它们包含%1,%2等,而不是一般(%d,%s ...)格式?

Please note, that the solution has to be generic enough for each possible message with each posible types\\ arguments order... 请注意,对于每个可能的消息,解决方案必须足够通用,每个可能的类型\\参数顺序...

Is it possible at all? 有可能吗?

Thank you. 谢谢。

First of all functions like printf support positioned format: 首先是printf支持定位格式的功能:

printf("%2$s, %1$d", salary, name);

For C++, beside the C solution there is a boost::format library: 对于C ++,除了C解决方案之外,还有一个boost::format库:

std::cout << boost::format("%2%, %1%") % salary % name;

Also if you are moving software to Linux I would suggest use "different" approach for localization: use either gettext or boost.locale library. 此外,如果您将软件移动到Linux,我建议使用“不同”的方法进行本地化:使用gettext或boost.locale库。

And instead of this: 而不是这个:

wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);

Use : 使用 :

C/gettext: C / gettext的:

snprintf(msg,sizeof(msg),gettext("This is the message to %1$s about %2$s"),who,what);

C++/gettext: C ++ / gettext的:

using boost::format;
std::ostringstream ss;
ss << format(gettext("This is the message to %1% about %2%")) % who % what;

C++ using boost.locale: 使用boost.locale的C ++:

using boost::locale::format;
using boost::locale::translate;
std::ostringstream ss;
ss << format(translate("This is the message to {1} about {2}")) % who % what;

The FormatMessage() function does use printf-style format specifiers; FormatMessage()函数使用printf样式的格式说明符; they go inside exclamation points after the %1 or whatever. 他们在%1或其他之后进入感叹号。 A placeholder with no format specifier is equivalent to a printf "%s." 没有格式说明符的占位符相当于printf“%s”。

What you'd need to do would be to transform the format strings a bit; 你需要做的是稍微改变格式字符串; change "%1" to "%1$s", "%2!u!" 将“%1”更改为“%1 $ s”,“%2!u!” to "%2$u", "%3!4.5e!" 到“%2 $ u”,“%3!4.5e!” to "%3$4.5e" and so on. 到“%3 $ 4.5e”等等。 Basically just change the ! 基本上只是改变了! characters around the format specifier into a single $ preceding it, and cope with the possibility of a bare "%number". 格式说明符周围的字符在它之前的单个$中,并且可以处理裸“%number”的可能性。

The problem with positional parameters like %1$anytype is that it must appear in the format string for a %2$anytype to work. 像%1 $ anytype这样的位置参数的问题是它必须出现在%2 $ anytype的格式字符串中才能工作。 Cf. 参看 printf("Today, %1$s received %2$d dollars in salary\\n", name, salary); vs. printf("Today, I received %2$d dollars in salary\\n", name, salary); printf("Today, I received %2$d dollars in salary\\n", name, salary); (boom). (繁荣)。 So it does not always work, like when the user is free to provide the format string, and decides to omit a field. 所以它并不总是有效,比如用户可以自由提供格式字符串,并决定省略字段。 In that case, a named parameter approach seems preferable. 在这种情况下,命名参数方法似乎更可取。 libHX for example provides such where you could use "%(SALARY) %(NAME)" . 例如,libHX提供了可以使用"%(SALARY) %(NAME)"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM