2

我有一个包含日语和拉丁字符混合的多字节字符串。我正在尝试将此字符串的一部分复制到单独的内存位置。由于它是一个多字节字符串,因此某些字符使用一个字节,而其他字符使用两个。复制部分字符串时,我不能复制“一半”日文字符。为了能够正确执行此操作,我需要能够确定多字节字符串字符的开始和结束位置。

例如,如果字符串包含需要 [2 字节][2 字节][1 字节] 的 3 个字符,我必须将 2、4 或 5 个字节复制到另一个位置,而不是 3,因为如果我要复制 3 我只会复制第二个字符的一半。

为了弄清楚多字节字符串字符的开始和结束位置,我尝试使用 Windows API 函数 CharNext 和 CharNextExA 但没有运气。当我使用这些函数时,它们一次一个字节地浏览我的字符串,而不是一次一个字符。根据 MSDN,CharNext 应该是CharNext 函数检索指向字符串中下一个字符的指针。.

下面是一些代码来说明这个问题:

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>

/* string consisting of six "asian" characters */
wchar_t wcsString[] = L"\u9580\u961c\u9640\u963f\u963b\u9644";

int main() 
{
   // Convert the asian string from wide char to multi-byte.
   LPSTR mbString = new char[1000];
   WideCharToMultiByte( CP_UTF8, 0, wcsString, -1, mbString, 100,  NULL, NULL);

   // Count the number of characters in the string.
   int characterCount = 0;
   LPSTR currentCharacter = mbString;
   while (*currentCharacter)
   {
      characterCount++;

     currentCharacter = CharNextExA(CP_UTF8, currentCharacter, 0);
   }
}

(请忽略内存泄漏和错误检查失败。)

现在,在上面的示例中,我希望 characterCount 变为 6,因为这是亚洲字符串中的字符数。但相反,characterCount 变为 18,因为 mbString 包含 18 个字符:

門阜陀阿阻附

我不明白它应该如何工作。CharNext 应该如何知道字符串中的“é–€é”是日本字符的编码版本,还是实际上是字符 é – € 和 é?

一些注意事项:

  • 我已经阅读了 Joels 的博客文章,了解每个开发人员需要了解的有关 Unicode 的内容。不过,我可能误解了其中的一些内容。
  • 如果我只想计算字符,我可以直接计算亚洲字符串中的字符。请记住,我的真正目标是将多字节字符串的一部分复制到单独的位置。单独的位置只支持多字节,不支持宽字符。
  • 如果我使用MultiByteToWideChar将mbString的内容转换回宽字符,我得到正确的字符串(门阜陀阿阻附),这表明mbString没有问题。

编辑:显然 CharNext 函数不支持 UTF-8,但微软忘记记录了。我将自己的例程扔/复制粘贴在一起,我不会使用,需要改进。我猜它很容易崩溃。

  LPSTR CharMoveNext(LPSTR szString)
  {
     if (szString == 0 || *szString == 0)
        return 0;

     if ( (szString[0] & 0x80) == 0x00)
        return szString + 1;
     else if ( (szString[0] & 0xE0) == 0xC0)
        return szString + 2;
     else if ( (szString[0] & 0xF0) == 0xE0)
        return szString + 3;
     else if ( (szString[0] & 0xF8) == 0xF0)
        return szString + 4;
     else
        return szString +1;
  }
4

5 回答 5

4

以下是对Sorting it All Out 博客中发生的事情的一个非常好的解释:CharNextExA 坏了吗?. 简而言之,CharNext 不是为处理 UTF8 字符串而设计的。

于 2009-08-05T21:51:45.627 回答
3

据我所知(谷歌和实验),CharNextExA它实际上不适用于 UTF-8,只支持使用较短前导/尾随字节对或单字节字符的多字节编码。

UTF-8 是一种相当常规的编码,有很多库可以做你想做的事,但也很容易推出你自己的。

在这里查看unicode.org,特别是表 3-7 中的有效序列形式。

const char* NextUtf8( const char* in )
{
    if( in == NULL || *in == '\0' )
        return in;

    unsigned char uc = static_cast<unsigned char>(*in);

    if( uc < 0x80 )
    {
        return in + 1;
    }
    else if( uc < 0xc2 )
    {
         // throw error? invalid lead byte
    }
    else if( uc < 0xe0 )
    {
        // check in[1] for validity( 0x80 .. 0xBF )
        return in + 2;
    }
    else if( uc < 0xe1 )
    {
        // check in[1] for validity( 0xA0 .. 0xBF )
        // check in[2] for validity( 0x80 .. 0xBF )
        return in + 3;
    }
    else // ... etc.
    // ...
}
于 2009-08-05T22:02:46.553 回答
3

鉴于CharNextExA 不适用于 UTF-8,您可以自己解析它。跳过前两位有 10 的字符。可以在 UTF-8 的定义中看到模式:http ://en.wikipedia.org/wiki/Utf-8

LPSTR CharMoveNext(LPSTR szString)
{
    ++szString;
    while ((*szString & 0xc0) == 0x80)
        ++szString;
    return szString;
}
于 2009-08-05T22:06:24.927 回答
0

尝试使用 932 作为代码页。我不认为 CP_UTF8 是一个真正的代码页,它可能只适用于 WideCharToMultibyte() 并返回。您也可以尝试 isleadByte(),但这需要正确设置语言环境或正确设置默认代码页。我已成功使用 IsDBCSLeadByteEx(),但从未使用 CP_UTF8。

于 2009-08-05T22:07:09.090 回答
0

这不是您问题的直接答案,但您可能会发现以下教程很有帮助,我当然做到了。事实上,这里提供的信息足以让您自己轻松遍历多字节字符串:

完整的字符串教程

于 2009-08-05T21:47:08.163 回答