将wchar_t转换为char

时间:2010-06-11 03:14:06

标签: c++

我想知道这样做是否安全?

wchar_t wide = /* something */;
assert(wide >= 0 && wide < 256 &&);
char myChar = static_cast<char>(wide);

如果我非常确定宽字符将落在ASCII范围内。

9 个答案:

答案 0 :(得分:27)

为什么不使用库例程wcstombs

答案 1 :(得分:10)

assert用于确保在调试模式下某些内容为真,而不会在发布版本中产生任何影响。最好使用if语句,并为超出范围的字符设置替代计划,除非获得超出范围的字符的唯一方法是通过程序错误。

此外,根据您的字符编码,您可能会发现Unicode字符0x80到0xff与其char版本之间存在差异。

答案 2 :(得分:9)

您正在寻找wctomb():它符合ANSI标准,因此您可以信赖它。即使wchar_t使用255以上的代码,它也能正常工作。您几乎肯定不想使用它。


wchar_t 是一个整数类型,所以如果您真的这样做,编译器就不会抱怨:

char x = (char)wc;

因为它是一个整体类型,所以绝对没有理由这样做。如果你不小心阅读了基于它的Herbert Schildt's C: The Complete Reference任何 C书,那么你就完全被误导了。 字符应为int 或更好类型。这意味着你应该写这个:

int x = getchar();

而不是这个:

char x = getchar(); /* <- WRONG! */

就整体类型而言,char毫无价值。您不应该创建带有char类型参数的函数,也不应该创建char类型的临时变量,同样的建议也适用于wchar_t

对于字符串,

char*可能是一个方便的typedef,但将其视为“字符数组”或“指向字符数组的指针”是一个新手错误 - 尽管{ {3}}工具说。把它当作一个真实的字符数组来处理,像这样废话:

for(int i = 0; s[i]; ++i) {
  wchar_t wc = s[i];
  char c = doit(wc);
  out[i] = c;
}

是荒谬的错误。它做你想做的事; 将以微妙和严肃的方式打破,在不同的平台上表现不同,而且肯定会混淆用户的地狱。如果您看到这一点,那么您正尝试重新实现cdecl已经是ANSI C的一部分,但它仍然是错误的

真的正在寻找wctombs(),它将一个字符串从一个编码(即使它被打包成一个wchar_t数组)转换为一个字符串另一种编码。

现在去阅读iconv(),了解iconv的错误。

答案 3 :(得分:5)

我写了一段简短的函数,将wchar_t数组打包到char数组中。不在ANSI代码页(0-127)上的字符被'?'替换字符,它正确处理代理对。

size_t to_narrow(const wchar_t * src, char * dest, size_t dest_len){
  size_t i;
  wchar_t code;

  i = 0;

  while (src[i] != '\0' && i < (dest_len - 1)){
    code = src[i];
    if (code < 128)
      dest[i] = char(code);
    else{
      dest[i] = '?';
      if (code >= 0xD800 && code <= 0xD8FF)
        // lead surrogate, skip the next code unit, which is the trail
        i++;
    }
    i++;
  }

  dest[i] = '\0';

  return i - 1;

}

答案 4 :(得分:2)

从技术上讲,“char”可以与“signed char”或“unsigned char”具有相同的范围。对于无符号字符,您的范围是正确的;从理论上讲,对于签名字符,你的情况是错误的。在实践中,很少有编译器会反对 - 结果将是相同的。

Nitpick:&&中的最后一个assert是语法错误。

断言是否合适取决于代码到达客户时是否能够承受崩溃,以及如果断言条件被违反但断言未编译到代码中,您可以或应该做什么。对于调试工作,它看起来很好,但您可能希望在运行时检查之后进行主动测试。

答案 5 :(得分:1)

还可以转换wchar_t - &gt; wstring - &gt; string - &gt; char

wchar_t wide;
wstring wstrValue;
wstrValue[0] = wide

string strValue;
strValue.assign(wstrValue.begin(), wstrValue.end());  // convert wstring to string

char char_value = strValue[0];

答案 6 :(得分:0)

一般来说,没有。 int(wchar_t(255)) == int(char(255))当然,但这只意味着它们具有相同的int值。它们可能不代表相同的字符。

即使是大多数Windows PC,您也会看到这种差异。例如,在Windows代码页1250上,char(0xFF)wchar_t(0x02D9)(上面的点)是相同的字符,而不是wchar_t(0x00FF)(带有分音符的小y)。

请注意,它甚至不适用于ASCII范围,因为C ++甚至不需要ASCII。特别是在IBM系统上,您可能会看到'A' != 65

答案 7 :(得分:0)

这是另一种方法,请记住在结果上使用free()。​​

char* wchar_to_char(const wchar_t* pwchar)
{
    // get the number of characters in the string.
    int currentCharIndex = 0;
    char currentChar = pwchar[currentCharIndex];

    while (currentChar != '\0')
    {
        currentCharIndex++;
        currentChar = pwchar[currentCharIndex];
    }

    const int charCount = currentCharIndex + 1;

    // allocate a new block of memory size char (1 byte) instead of wide char (2 bytes)
    char* filePathC = (char*)malloc(sizeof(char) * charCount);

    for (int i = 0; i < charCount; i++)
    {
        // convert to char (1 byte)
        char character = pwchar[i];

        *filePathC = character;

        filePathC += sizeof(char);

    }
    filePathC += '\0';

    filePathC -= (sizeof(char) * charCount);

    return filePathC;
}

答案 8 :(得分:0)

一种简单的方法是:

        wstring your_wchar_in_ws(<your wchar>);
        string your_wchar_in_str(your_wchar_in_ws.begin(), your_wchar_in_ws.end());
        char* your_wchar_in_char =  your_wchar_in_str.c_str();

我使用这种方法多年了:)