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

发布于 2024-10-09 12:47:17 字数 2772 浏览 2 评论 0原文

作为这个问题的后续问题(消息编译器替换在 Linux gcc 中),我遇到以下问题:

在 Windows 上使用 MC.exe 编译和生成消息时,在 C++ 代码中我调用 FormatMessage,它检索消息并使用 va_list *Arguments 参数发送不同的消息参数。 例如:
messages.mc 文件:

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

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 );
}

调用者代码:

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

现在,我编写了一个简单的脚本来从 .mc 文件生成 .msg 文件,然后使用 gencat 从中生成目录。

但是有没有办法使用格式化字符串,因为它们包含 %1、%2 等,而不是通用 (%d、%s...) 格式?

请注意,对于每个可能的消息以及每个可能的类型\参数顺序,该解决方案必须足够通用......

有可能吗?

谢谢。

As a follow-up of this question (Message compiler replacement in Linux gcc), I have the following problem:

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.
For example:
messages.mc file:

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

C++ code:

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.

But is there a way to use the formatted strings as they contain %1, %2, etc. and NOT the general (%d, %s...) format?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

靑春怀旧 2024-10-16 12:47:18

像 %1$anytype 这样的位置参数的问题是,它必须出现在格式字符串中,%2$anytype 才能工作。比照。 printf("今天,%1$s 收到了 %2$d 美元的薪水\n",姓名,薪水);printf("今天,我收到了 %2$d 美元在工资\n",姓名,工资);(繁荣)。因此,它并不总是有效,例如当用户可以自由提供格式字符串并决定省略字段时。在这种情况下,命名参数方法似乎更可取。例如,libHX 提供了这样的地方,您可以使用 "%(SALARY) %(NAME)"

The problem with positional parameters like %1$anytype is that it must appear in the format string for a %2$anytype to work. 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); (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)".

多情癖 2024-10-16 12:47:17

首先,像 printf 这样的函数支持定位格式:

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

对于 C++,除了 C 解决方案之外,还有一个 boost::format 库:

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

此外,如果您要将软件迁移到 Linux,我建议使用“不同”的方法本地化:使用 gettext 或 boost.locale 库。

而不是这个:

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

使用:

C/gettext:

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

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:

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

First of all functions like printf support positioned format:

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

For C++, beside the C solution there is a boost::format library:

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.

And instead of this:

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

Use :

C/gettext:

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

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:

using boost::locale::format;
using boost::locale::translate;
std::ostringstream ss;
ss << format(translate("This is the message to {1} about {2}")) % who % what;
烂柯人 2024-10-16 12:47:17

FormatMessage() 函数确实使用 printf 样式的格式说明符;它们在 %1 或其他后面的感叹号内。没有格式说明符的占位符相当于 printf“%s”。

您需要做的就是稍微转换一下格式字符串;将“%1”更改为“%1$s”、“%2!u!”至“%2$u”、“%3!4.5e!”到“%3$4.5e”等等。基本上只是改变!将格式说明符周围的字符转换为其前面的单个 $,并处理裸露的“%number”的可能性。

The FormatMessage() function does use printf-style format specifiers; they go inside exclamation points after the %1 or whatever. A placeholder with no format specifier is equivalent to a printf "%s."

What you'd need to do would be to transform the format strings a bit; change "%1" to "%1$s", "%2!u!" to "%2$u", "%3!4.5e!" to "%3$4.5e" and so on. Basically just change the ! characters around the format specifier into a single $ preceding it, and cope with the possibility of a bare "%number".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文