我该如何比较wstring
(如L"Hello"
)和string
类型的字符串?如果需要它们的类型相同,又该如何进行转换?
我该如何比较wstring
(如L"Hello"
)和string
类型的字符串?如果需要它们的类型相同,又该如何进行转换?
既然您提出了要求,这里是我的标准字符串转宽字符串的转换函数,使用了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
无法正常工作。您可以使用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转换为确定的编码方式。 - Kerrek SBman 3 iconv
获取简介。您必须按顺序使用 iconv_open()
、iconv()
和 iconv_close()
。 - Kerrek SBstring
转换为wstring
,然后使用CompareStringEx进行比较。mbstowcs
和wcscmp
。标准的宽字符C++函数在Windows下通常不可移植;例如mbstowcs
已被弃用。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];
}
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]
的使用是否符合标准规范,如果有人对此有好的答案,请在评论中告诉我。此外,有一个默认假设是转换后的字符串长度(以wchar_t
为单位)不会长于原始字符串长度(以char
为单位)- 这是一个逻辑上的假设,但我仍然不确定它是否符合标准。
另一方面,似乎没有办法询问mbstowcs
所需缓冲区的大小,因此要么使用这种方法,要么使用Unicode库(如Windows API或类似iconv的库)提供更好的代码(更好地定义)。
还要记住,在不使用特殊函数比较Unicode字符串时,可能会遇到问题,两个等效的字符串可能会在按位比较时评估出现差异。
长话短说:这应该可以工作,我认为这是您只使用标准库可以做到的最大程度,但实现方式与Unicode处理方式的关系很大,并且我不太信任它。一般来说,最好在应用程序内部使用编码,并避免这种类型的转换,除非绝对必要,并且如果您正在使用明确的编码,请使用不太依赖于实现的API。
wstring
,即使可以,也会得到错误长度的字符串。你需要调用mbstowcs
两次以获取所需的目标长度。 - Kerrek SBmbstowcs
完全与编码无关,它仅在“系统的多字节表示”和“系统的宽字符”之间进行转换。对于这两者中的任何一个,都没有要求必须是您熟悉的内容。如果您想要确定的编码,则必须使用iconv()
从WCHAR转换为您喜欢的(Unicode?)编码。这是我对该主题的小抱怨。 - Kerrek SBstd::wstring
的想法是为了避免浪费时间进行另一次堆分配/清除,如果转换经常发生,可能会影响性能。问题是,尽管我看到这种习惯用法不止一次,但我不确定那个东西是否实际上被标准允许。 - Matteo Italia
mbstowcs()
将字符串转换为宽字符串。但是,你必须使用char*
进行操作。 - Kerrek SB