c 读取非 ASCII 字符
c reading non ASCII characters
我正在解析一个包含 æ ø å 等字符的文件。如果我们假设我已经存储了一行文本文件,如下所示
#define MAXLINESIZE 1024
char* buffer = malloc(MAXLINESIZE)
...
fgets(buffer,MAXLINESIZE,handle)
...
如果我想计算 一行中的字符数。如果我尝试执行以下操作:
char* p = buffer
int count = 0;
while (*p != '\n') {
if (isgraph(*p)) {
count++;
}
p++;
}
这会忽略任何出现的 æ ø å
即:计算 "aåeæioøu" 会 return 5 而不是 8
我需要以其他方式读取文件吗?我不应该使用 char*
而是 int*
吗?
C标准IO库只能读取字节。您的文件可能包含多字节字符,使用 UTF8 或其他编码进行编码。您需要一个库来解释此类文件。
您的文件可能包含 Latin1 文本,在这种情况下字符是字节。在这种情况下,除非您设置了正确的语言环境,否则您不能使用 isgraph
。
底线:找到文件中使用的编码。然后相应地阅读它。无论如何,纯 C 不知道编码。
您需要了解您的字符使用了哪种编码。我猜很有可能是 UTF-8 (and you should use UTF8 everywhere....), read Joel's blog on Unicode. If your encoding is not UTF-8 you should convert it to UTF-8 e.g. using libiconv.
那么您需要一个用于 UTF-8 的 C 库。有很多(但 none 在 C11 language yet). I recommend libunistring or glib (from GTK), but see also this 中标准化。
您的代码会发生变化,因为一个 UTF-8 字符可以占用一到四个 [8 位] 字节(但维基百科 UTF-8 page mentions 6 bytes at most; See Unicode 标准了解详细信息)。你不会测试一个字节(即一个普通的 C char
)是否是一个字母,但是如果一个字节和它后面的几个字节(由指针给出,即 char*
或更好的 uint8_t*
) 编码一个字母(包括西里尔字母等)。
并非每个字节序列都是有效的 UTF-8 表示形式,您可能希望在分析某行(或以 null 结尾的 C 字符串)之前验证它。
假设您使用 UTF-8。
你需要了解how UTF-8 works。
这是一个小作品,可以满足您的需求:
int nbChars(char *str) {
int len = 0;
int i = 0;
int charSize = 0; // Size of the current char in byte
if (!str)
return -1;
while (str[i])
{
if (charSize == 0)
{
++len;
if (!(str[i] >> 7 & 1)) // ascii char
charSize = 1;
else if (!(str[i] >> 5 & 1))
charSize = 2;
else if (!(str[i] >> 4 & 1))
charSize = 3;
else if (!(str[i] >> 3 & 1))
charSize = 4;
else
return -1; // not supposed to happen
}
else if (str[i] >> 6 & 3 != 2)
return -1;
--charSize;
++i;
}
return len;
}
它 returns 字符数,如果它不是有效的 UTF-8 字符串则为 -1。
(无效的 UTF-8 字符串,我的意思是格式无效。我不检查字符是否实际存在)
编辑:如评论部分所述,此代码不处理分解的 unicode
我正在解析一个包含 æ ø å 等字符的文件。如果我们假设我已经存储了一行文本文件,如下所示
#define MAXLINESIZE 1024
char* buffer = malloc(MAXLINESIZE)
...
fgets(buffer,MAXLINESIZE,handle)
...
如果我想计算 一行中的字符数。如果我尝试执行以下操作:
char* p = buffer
int count = 0;
while (*p != '\n') {
if (isgraph(*p)) {
count++;
}
p++;
}
这会忽略任何出现的 æ ø å
即:计算 "aåeæioøu" 会 return 5 而不是 8
我需要以其他方式读取文件吗?我不应该使用 char*
而是 int*
吗?
C标准IO库只能读取字节。您的文件可能包含多字节字符,使用 UTF8 或其他编码进行编码。您需要一个库来解释此类文件。
您的文件可能包含 Latin1 文本,在这种情况下字符是字节。在这种情况下,除非您设置了正确的语言环境,否则您不能使用 isgraph
。
底线:找到文件中使用的编码。然后相应地阅读它。无论如何,纯 C 不知道编码。
您需要了解您的字符使用了哪种编码。我猜很有可能是 UTF-8 (and you should use UTF8 everywhere....), read Joel's blog on Unicode. If your encoding is not UTF-8 you should convert it to UTF-8 e.g. using libiconv.
那么您需要一个用于 UTF-8 的 C 库。有很多(但 none 在 C11 language yet). I recommend libunistring or glib (from GTK), but see also this 中标准化。
您的代码会发生变化,因为一个 UTF-8 字符可以占用一到四个 [8 位] 字节(但维基百科 UTF-8 page mentions 6 bytes at most; See Unicode 标准了解详细信息)。你不会测试一个字节(即一个普通的 C char
)是否是一个字母,但是如果一个字节和它后面的几个字节(由指针给出,即 char*
或更好的 uint8_t*
) 编码一个字母(包括西里尔字母等)。
并非每个字节序列都是有效的 UTF-8 表示形式,您可能希望在分析某行(或以 null 结尾的 C 字符串)之前验证它。
假设您使用 UTF-8。
你需要了解how UTF-8 works。
这是一个小作品,可以满足您的需求:
int nbChars(char *str) {
int len = 0;
int i = 0;
int charSize = 0; // Size of the current char in byte
if (!str)
return -1;
while (str[i])
{
if (charSize == 0)
{
++len;
if (!(str[i] >> 7 & 1)) // ascii char
charSize = 1;
else if (!(str[i] >> 5 & 1))
charSize = 2;
else if (!(str[i] >> 4 & 1))
charSize = 3;
else if (!(str[i] >> 3 & 1))
charSize = 4;
else
return -1; // not supposed to happen
}
else if (str[i] >> 6 & 3 != 2)
return -1;
--charSize;
++i;
}
return len;
}
它 returns 字符数,如果它不是有效的 UTF-8 字符串则为 -1。
(无效的 UTF-8 字符串,我的意思是格式无效。我不检查字符是否实际存在)
编辑:如评论部分所述,此代码不处理分解的 unicode