Compare std::wstring and std::string

Compare std::wstring and std::string

本文关键字:std string and wstring Compare      更新时间:2023-10-16

如何比较wstring,例如L"Hello"string ?如果我需要相同的类型,我如何将它们转换为相同的类型?

既然你问了,这是我的标准转换函数从字符串到宽字符串,使用c++ std::stringstd::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);
}

往回看,从宽弦变成窄弦。我将窄字符串称为"locale string",因为它是依赖于平台的编码,取决于当前的locale:

// 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风格的转换函数mbsrtowcswcsrtombs在Windows上不能正常工作。在那里,你可以使用mbstowcswcstombs代替:mbstowcs(buf.data(), cs, wn + 1);, wcstombs(buf.data(), cs, wn + 1);


回答你的问题,如果你想比较两个字符串,你可以将它们都转换为宽字符串,然后比较它们。如果你正在从磁盘读取一个已知编码的文件,你应该使用iconv()将文件从已知编码转换为WCHAR,然后与宽字符串进行比较。

但是要注意,复杂的Unicode文本可能有多个不同的表示,作为您可能希望认为相等的码点序列。如果可能的话,您需要使用更高级的Unicode处理库(如ICU),并将字符串规范化为某种通用的、可比较的形式。

您应该使用mbstowcschar字符串转换为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 s的数量更长(以CC_24 s为单位)——这是一个逻辑假设,我仍然不确定它是否被标准所涵盖。

另一方面,似乎没有办法要求mbstowcs所需缓冲区的大小,所以你要么这样做,要么从Unicode库(无论是Windows api还是像iconv这样的库)中使用(更好地完成和更好地定义)代码。

但是,请记住,在不使用特殊函数的情况下比较Unicode字符串是很危险的,两个等效字符串在按位比较时可能求值不同。

长话短说:这应该工作,我认为这是你能做的最大的标准库,但它是很多实现依赖于Unicode是如何处理的,我不会相信它很多。一般来说,最好在应用程序中使用一种编码,除非绝对必要,否则避免这种转换,并且,如果您正在使用确定的编码,请使用与实现依赖程度较低的api。

在这样做之前要三思——您可能一开始就不想比较它们。如果你确定你这样做,你正在使用Windows,然后用MultiByteToWideChar转换stringwstring,然后与CompareStringEx比较。

如果您不使用Windows,那么类似的函数是mbstowcswcscmp。标准的宽字符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];
}