【发布时间】:2011-08-21 21:20:29
【问题描述】:
如何将wstring(例如L"Hello")与string 进行比较?如果我需要相同的类型,如何将它们转换为相同的类型?
【问题讨论】:
-
使用
mbstowcs()将字符串变成宽字符串。不过,您必须使用char*s。 -
您是否考虑过只使用 std::wstring?
标签: c++
如何将wstring(例如L"Hello")与string 进行比较?如果我需要相同的类型,如何将它们转换为相同的类型?
【问题讨论】:
mbstowcs() 将字符串变成宽字符串。不过,您必须使用 char*s。
标签: c++
既然你问了,这是我从字符串到宽字符串的标准转换函数,使用 C++ std::string 和 std::wstring 类实现。
首先,确保以set_locale 启动您的程序:
#include <clocale>
int main()
{
std::setlocale(LC_CTYPE, ""); // before any string operations
}
现在是函数。首先,从窄字符串中获取宽字符串:
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cwchar>
#include <cerrno>
// Dummy overload
std::wstring get_wstring(const std::wstring & s)
{
return s;
}
// Real worker
std::wstring get_wstring(const std::string & s)
{
const char * cs = s.c_str();
const size_t wn = std::mbsrtowcs(NULL, &cs, 0, NULL);
if (wn == size_t(-1))
{
std::cout << "Error in mbsrtowcs(): " << errno << std::endl;
return L"";
}
std::vector<wchar_t> buf(wn + 1);
const size_t wn_again = std::mbsrtowcs(buf.data(), &cs, wn + 1, NULL);
if (wn_again == size_t(-1))
{
std::cout << "Error in mbsrtowcs(): " << errno << std::endl;
return L"";
}
assert(cs == NULL); // successful conversion
return std::wstring(buf.data(), wn);
}
然后回过头来,用宽弦制作窄弦。我将窄字符串称为“语言环境字符串”,因为它是依赖于平台的编码,具体取决于当前语言环境:
// Dummy
std::string get_locale_string(const std::string & s)
{
return s;
}
// Real worker
std::string get_locale_string(const std::wstring & s)
{
const wchar_t * cs = s.c_str();
const size_t wn = std::wcsrtombs(NULL, &cs, 0, NULL);
if (wn == size_t(-1))
{
std::cout << "Error in wcsrtombs(): " << errno << std::endl;
return "";
}
std::vector<char> buf(wn + 1);
const size_t wn_again = std::wcsrtombs(buf.data(), &cs, wn + 1, NULL);
if (wn_again == size_t(-1))
{
std::cout << "Error in wcsrtombs(): " << errno << std::endl;
return "";
}
assert(cs == NULL); // successful conversion
return std::string(buf.data(), wn);
}
一些注意事项:
std::vector::data(),你可以改成&buf[0]。r 风格的转换函数mbsrtowcs 和wcsrtombs 在Windows 上不能正常工作。在那里,您可以改用mbstowcs 和wcstombs:mbstowcs(buf.data(), cs, wn + 1);、wcstombs(buf.data(), cs, wn + 1);
针对您的问题,如果您想比较两个字符串,您可以将它们都转换为宽字符串,然后进行比较。如果您正在从磁盘读取具有已知编码的文件,则应使用iconv() 将文件从已知编码转换为 WCHAR,然后与宽字符串进行比较。
但请注意,复杂的 Unicode 文本可能有多个 不同 表示作为代码点序列,您可能会认为它们是相等的。如果有可能,您需要使用更高级别的 Unicode 处理库(例如 ICU)并将您的字符串规范化为一些常见的、可比较的形式。
【讨论】:
string 和 wstring 类以及 mbstowcs/wcstombs 完全与编码无关。您无法控制任何字符串的最终编码方式。如果您需要明确的编码,则需要使用 iconv() 之类的东西从 WCHAR 转换为明确的编码。
man 3 iconv 以获得概要。您必须按顺序使用iconv_open()、iconv() 和iconv_close()。
您应该使用mbstowcs 将char 字符串转换为wchar_t 字符串,然后比较生成的字符串。请注意,mbstowcs 适用于 char */wchar *,因此您可能需要执行以下操作:
std::wstring StringToWstring(const std::string & source)
{
std::wstring target(source.size()+1, L' ');
std::size_t newLength=std::mbstowcs(&target[0], source.c_str(), target.size());
target.resize(newLength);
return target;
}
我不完全确定&target[0] 的使用是否完全符合标准,如果有人对此有很好的答案,请在 cmets 中告诉我。此外,还有一个隐含的假设,即转换后的字符串(wchar_ts 的数量)不会比原始字符串的chars 的数量长 - 这是一个我仍然不确定它是否被涵盖的逻辑假设标准。
另一方面,似乎没有办法向 mbstowcs 询问所需缓冲区的大小,所以要么你走这条路,要么使用 Unicode 库中的(做得更好和定义更好的)代码(be它是 Windows API 或 iconv 等库)。
不过,请记住,在不使用特殊函数的情况下比较 Unicode 字符串很容易出错,两个等价的字符串在按位比较时可能会得出不同的结果。
长话短说:这应该可行,而且我认为这是您仅使用标准库所能做的最大工作,但在处理 Unicode 方面很大程度上取决于实现,我不太相信它。一般来说,最好在应用程序中坚持编码并避免这种转换,除非绝对必要,而且,如果您使用明确的编码,请使用不太依赖于实现的 API。
【讨论】:
wstring 不能在单个数字上构造,即使可以,您最终也会得到长度错误的字符串。您确实必须调用mbstowcs 两次才能获得所需的目标长度。
mbstowcs 完全与编码无关。它只是在“系统的多字节表示”和“系统的宽字符”之间进行转换。没有要求其中任何一个是你能识别的任何东西。如果你想要一个明确的编码,你必须用iconv() 将WCHAR 转换为你最喜欢的(Unicode?)编码。这是我关于这个主题的little rant。
std::wstring 的想法是避免在另一个堆分配/释放上浪费时间,如果经常进行转换,可能会影响性能。问题是,虽然我不止一次看到这个成语,但我不确定这件事是否真的被标准允许。
在做这件事之前要三思而后行——你可能一开始就不想比较它们。如果您确定这样做并且您使用的是 Windows,则将 string 转换为 wstring 和 MultiByteToWideChar,然后与 CompareStringEx 进行比较。
如果您不使用 Windows,则类似的功能是 mbstowcs 和 wcscmp。标准的宽字符 C++ 函数在 Windows 下通常不可移植;例如 mbstowcs 已弃用。
使用 Unicode 的跨平台方式是使用 ICU 库。
注意使用特殊函数进行 Unicode 字符串比较,不要手动进行。两个 Unicode 字符串可以有不同的字符,但仍然是相同的。
wstring ConvertToUnicode(const string & str)
{
UINT codePage = CP_ACP;
DWORD flags = 0;
int resultSize = MultiByteToWideChar
( codePage // CodePage
, flags // dwFlags
, str.c_str() // lpMultiByteStr
, str.length() // cbMultiByte
, NULL // lpWideCharStr
, 0 // cchWideChar
);
vector<wchar_t> result(resultSize + 1);
MultiByteToWideChar
( codePage // CodePage
, flags // dwFlags
, str.c_str() // lpMultiByteStr
, str.length() // cbMultiByte
, &result[0] // lpWideCharStr
, resultSize // cchWideChar
);
return &result[0];
}
【讨论】: