对于下面的程序,我希望得到5而不是10。有人知道如何修复代码来计算多字节字符的数量吗?谢谢
/* vim: set noexpandtab tabstop=2 shiftwidth=2 softtabstop=-1 fileencoding=utf-8: */
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>
size_t nchars(const char *s) {
size_t charlen, chars;
mbstate_t mbs;
chars = 0;
memset(&mbs, 0, sizeof(mbs));
while (
(charlen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0
&& charlen != (size_t)-1
&& charlen != (size_t)-2
) {
s += charlen;
chars++;
}
return (chars);
}
int main() {
setlocale(LC_CTYPE, "en_US.utf8");
char * text = "öçşğü";
printf("%zun", nchars (text));
return 0;
}
$ ./main.exe
10
mbsinit
函数而不是memcpy
初始化类型为mbstate_t
的对象。所有字节零mbsinit
不保证表示初始移位状态,甚至不保证表示任何有效移位状态。
代码的主要问题围绕着这样一个事实:它正在分析字符串文字,其表示方式是在编译时根据源文件中这些字符的实际编码、编译器的源字符集中的表示方式以及编译器选择的执行字符集来确定的。您不能任意选择LC_CTYPE
——它必须与mb转换函数所需的数据相匹配。
C没有为程序定义一种机制来识别其LC_TYPE
对应于执行字符集的区域设置,甚至不要求存在这样的区域设置。然而,编译器的文档应该描述源字符和执行字符之间的映射,可能是根据区域设置或众所周知的编码,甚至可能描述指定的方法。编译器的文档还可能描述一种方法,用于指定它应该为源文件假设的编码。
此外,Unicode还有一个潜在的问题,即您作为一个人所认为的"字符"与表示它的Unicode字符之间可能不匹配。一般来说,这涉及到带有变音标记的字符,例如重音符号。其中许多更常用的都有一个单独的字符"组合"表示,但也可以表示为一个基本字符加上一个或多个组合字符的序列。
mbrlen()
不太可能区分基本字符和组合字符,因此即使没有任何编码混乱,您观察到的结果也可能是源文件中以分解形式表示的字符,或者编译器将其转换为该形式。
最重要的是,您的程序取决于标准没有指定的环境和实现特征,因此,它可能会在不同的实现中表现得不同,这似乎确实是观察到的。例如,您的特殊观察结果可能来自于以UTF-8编码的源文件,编译器假设它以单字节编码(如ISO-8859-1)编码,但编译器使用UTF-8作为其执行字符集。
如果您确保编译器根据源文件的实际编码来解释源文件,并且使用UTF-8作为执行字符集,那么您的方法可能不会发生更改。或者,在C11或更高版本中,您可以通过使用UTF-8文字来确保特定字符串的运行时编码为UTF-8,如下所示:
char * text = u8"öçşğü";
然而,这只涉及执行端的编码。您仍然需要将源文件编码与编译器期望的实际编码相匹配,并且您仍然可能受到预合成字符和分解字符之间差异的影响。