我需要实现一个函数来格式化宽string,并返回std :: wstring。 我的实现是:
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; }
它在Windows上运行良好,但不幸的是它不能在osx上工作,因为vswprintf(nullptr,0,fmt,va)将返回-1。
只有std可以使用,我不能假定string长度在一定范围内。
我想应该有办法做到这一点,但我找不到,你能帮忙吗? 谢谢
看来达尔文版本实际上是按照vfwprintf
的规范vfwprintf
。 不幸的是,似乎还没有一种方便的方法来计算一个宽格式化字符串的长度,就像传统的单字节字符串一样(通过传递一个NULL
目标字符串)。
经过大量的研究,我发现似乎有两个可行的解决方案,这两个在这个答案中提到:
我不喜欢迭代分配的方法,因为它似乎效率低下,盲目尝试多次相同的操作,直到你有足够的分配。
我的首选解决方案是将字符串(一次!)格式化为/dev/null
,以便计算长度,然后使用它来调整缓冲区大小。 这是必要的复制变量参数列表来做到这一点,因为它是由vs *函数消耗。 这是最接近你如何做,无论如何,只是一个空的流,而不是一个空字符串。
我提出的解决方案是:
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; }
我使用以下代码在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; }
伪代码:
buffer.resize(1) loop: res = vswprintf(buffer, args) if res indicates success: break buffer.resize(buffer.size() * 2) buffer.truncate()
换句话说,您只需尝试增加缓冲区大小,直到成功。 不过,我会从大于1个字符的东西开始,基本上这样大部分的调用都不需要重复,但这只是性能调优。
一个可能的解决方案可能是使用C11标准函数vswprintf_s
函数。 这不是完全可移植的,因为C11使得这个功能被实现是可选的,但是如果你的实现实现了它,我会提到它。
C ++ 14只指定了C99标准库,而这个函数在C11中。 但是如果你的C ++编译器的相关C编译器支持C11,那么它很可能使这个函数直接在C ++中可用。 即使不这样做,也可以将这些代码封装在一个C函数中,然后从C ++调用这个C函数。
vswprintf_s
函数对vswprintf_s
使用不同的参数,因此请仔细检查其文档。