我有一个包含日语和拉丁字符混合的多字节字符串。我正在尝试将此字符串的一部分复制到单独的内存位置。由于它是一个多字节字符串,因此某些字符使用一个字节而其他字符使用两个字符。复制字符串的部分时,我不能复制“半”日文字符。为了能够正确地执行此操作,我需要能够确定多字节字符串字符的开始和结束位置。
例如,如果字符串包含需要[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如何知道字符串中的“é-€”是日语字符的编码版本,还是字符é - €和é?
一些注意事项:
编辑: 显然,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;
}
答案 0 :(得分:4)
以下是对Sorting it All Out blog Is CharNextExA broken?所发生情况的一个非常好的解释。简而言之,CharNext不适用于UTF8字符串。
答案 1 :(得分: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.
// ...
}
答案 2 :(得分:2)
鉴于CharNextExA doesn't work with UTF-8,您可以自己解析它。只需跳过前两位中有10个字符的字符。您可以在UTF-8的定义中看到模式:http://en.wikipedia.org/wiki/Utf-8
LPSTR CharMoveNext(LPSTR szString)
{
++szString;
while ((*szString & 0xc0) == 0x80)
++szString;
return szString;
}
答案 3 :(得分:0)
这不是您问题的直接答案,但您可能会发现以下教程很有帮助,我当然也这样做了。实际上,这里提供的信息足以使您能够轻松地自己遍历多字节字符串:
答案 4 :(得分:0)
尝试使用932代码页。我不认为CP_UTF8是真正的代码页,它可能只适用于WideCharToMultibyte()和返回。您也可以尝试使用isleadByte(),但这需要正确设置区域设置或正确设置默认代码页。我已经成功使用了IsDBCSLeadByteEx(),但从未使用过CP_UTF8。