对于以下程序,我希望获得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("%zu\n", nchars (text));
return 0;
}
$ ./main.exe
10
答案 0 :(得分:2)
第二个问题:您应该通过mbstate_t
函数而不是mbsinit
来初始化类型为memcpy
的对象。不能保证全字节为零的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"öçşğü";
但是,这仅涉及执行端编码。您仍然需要将源文件编码与编译器期望的实际编码进行匹配,并且仍然会受到预组合字符和分解后字符之间差异的影响。