比较std :: wstring和std :: string

时间:2011-08-21 21:20:30

标签: c++

如何将wstring(例如L"Hello")与string进行比较?如果我需要具有相同的类型,我如何将它们转换为相同的类型?

3 个答案:

答案 0 :(得分:6)

因为您问过,这是我的标准转换函数,从字符串到宽字符串,使用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”,因为它取决于当前的语言环境,采用依赖于平台的编码:

// 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上无法正常运行。在那里,您可以改为使用mbstowcswcstombsmbstowcs(buf.data(), cs, wn + 1);wcstombs(buf.data(), cs, wn + 1);


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

请注意,复杂的Unicode文本可能有多个不同的表示作为您可能需要考虑的代码点序列。如果可能,您需要使用更高级别的Unicode处理库(例如ICU)并将字符串规范化为一些常见的可比较形式。

答案 1 :(得分:3)

您应该使用charwchar_t字符串转换为mbstowcs字符串,然后比较生成的字符串。请注意,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 s) - 这是一个逻辑假设,我仍然不是确定它符合标准。

另一方面,似乎无法向mbstowcs询问所需缓冲区的大小,因此要么采用这种方式,要么使用(更好地完成和更好定义的)Unicode库中的代码(无论是Windows API还是像iconv这样的库)。

尽管如此,请记住,在不使用特殊功能的情况下比较Unicode字符串是很滑的,当按位比较时,两个等效的字符串可能会有不同的评估。

长话短说:这应该可以工作,我认为这是标准库所能达到的最大值,但它在很大程度上取决于Unicode的处理方式,我不会相信它。一般来说,最好在应用程序中坚持使用编码并避免这种转换,除非绝对必要,并且,如果您正在使用明确的编码,请使用与实现相关性较低的API。

答案 2 :(得分:2)

在执行此操作之前请三思而后行 - 您可能不想首先比较它们。如果您确定要使用Windows,请使用MultiByteToWideCharstring转换为wstring,然后与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];
}