简体   繁体   English

如何在OSX上通过vswprintf格式化宽字符串(想要返回std :: wstring)

[英]How to format wide char string via vswprintf on OSX (want to return std::wstring)

I need implement a function to format wide char string and return std::wstring. 我需要实现一个函数来格式化宽字符串并返回std :: wstring。 My implementation is: 我的实施是:

std::wstring format(const wchar_t* fmt, ...)
{
    std::wstring ret;

    va_list va;
    va_start(va, fmt);

    int size = vswprintf(nullptr, 0, fmt, va);
    if (size > 0)
    {
        ret.resize(size + 1);
        vswprintf(&ret[0], size + 1, fmt, va);
    }

    va_end(va);
    return ret;
}

it works well on windows, but unfortunately it doesn't work on osx because vswprintf(nullptr, 0, fmt, va) will return -1. 它在Windows上运行良好,但不幸的是它在osx上不起作用,因为vswprintf(nullptr,0,fmt,va)将返回-1。

only std can be used and I can't assume the string length is in a certain range. 只能使用std,我不能假设字符串长度在一定范围内。

I think there should be some way to do that, but I can't find, can you help? 我认为应该有一些方法可以做到这一点,但我找不到,你能帮忙吗? thanks 谢谢

It appears that the Darwin version is actually behaving according to the specification for vfwprintf . 似乎Darwin版本实际上是根据vfwprintf的规范vfwprintf Unfortunately, it also seems there is no convenient way to calculate the length of a wide formatted string in the same way you would a regular single byte string (by passing a NULL destination string). 不幸的是,似乎没有方便的方法来计算格式字符串的长度,就像使用常规单字节字符串一样(通过传递NULL目标字符串)。

After much researching, I have found there seem to be two viable solutions, both of which are mentioned in this answer: 经过大量研究后,我发现似乎有两种可行的解决方案,这两种解决方案都在这个答案中提到:

I dislike the iterative allocation approach as it seems inefficient to blindly attempt the same operation many times until you have allocated enough. 我不喜欢迭代分配方法,因为在分配足够多的情况下盲目地尝试相同的操作似乎是低效的。

My preferred solution is to format the string (once!) to /dev/null first in order to calculate the length, and then use this to size your buffer. 我首选的解决方案是首先将字符串(一次!)格式化为/dev/null以计算长度,然后使用它来调整缓冲区的大小。 It is necessary to copy the variable args list to do this, as it is consumed by the vs*functions. 有必要复制变量args列表来执行此操作,因为它由vs *函数使用。 This is closest to how you were doing it anyway, just with a null stream instead of a null string. 这最接近你如何做它,只是使用空流而不是空字符串。

My proposed solution is thus: 我建议的解决方案是:

std::wstring format(const wchar_t* fmt, ...)
{
    std::wstring ret;

    va_list va;
    va_start(va, fmt);

    va_list vc;
    va_copy(vc, va);

    FILE* fp = fopen("/dev/null", "w");
    int size = vfwprintf(fp, fmt, vc);
    fclose(fp);

    if (size > 0)
    {
        ret.resize(size + 1);
        vswprintf(&ret[0], size + 1, fmt, va);
    }

    va_end(va);
    va_end(vc);

    return ret;
}

I tested this on Mac OS X with the following code, and observed the expected output: 我在Mac OS X上用以下代码测试了这个,并观察了预期的输出:

int main(int argc, char* argv[])
{
    auto outstr = format(L"Hello %ls, you are %u years old.\n", L"Fred", 65);

    std::wcout << outstr << std::endl;

    return 0;
}

Pseudocode: 伪代码:

buffer.resize(1)
loop:
    res = vswprintf(buffer, args)
    if res indicates success:
        break
    buffer.resize(buffer.size() * 2)
buffer.truncate()

In other words, you simply try with increasing buffer sizes until you succeed. 换句话说,您只需尝试增加缓冲区大小,直到成功为止。 I'd start off with something larger than 1 char though, basically so that most calls don't have to be repeated, but that's just performance finetuning. 我会从大于1个字符的东西开始,基本上这样大多数电话都不必重复,但这只是性能微调。

One possible solution might be to use the C11 standard function vswprintf_s function. 一种可能的解决方案可能是使用C11标准函数vswprintf_s函数。 This is not fully portable because C11 makes it optional for this function to be implemented, but I mention it in case your implementation does implement it. 这不是完全可移植的,因为C11使得它可以实现这个功能,但我提到它以防你的实现实现它。

C++14 only specifies the C99 standard library, and this function is in C11. C ++ 14仅指定C99标准库,此函数在C11中。 But if your C++ compiler's associated C compiler supports C11 then it is likely to make the function directly available from C++. 但是,如果您的C ++编译器的相关C编译器支持C11,那么它可能会使该函数直接从C ++中获得。 Even if it doesn't, you could wrap that code in a C function and call that C function from C++. 即使它没有,你可以将该代码包装在C函数中并从C ++调用该C函数。

The vswprintf_s function uses different arguments to vswprintf so check its documentation carefully. vswprintf_s函数使用不同的参数来vswprintf所以仔细检查其文档。

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

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