C读取非ASCII字符

5

我正在解析一个包含æøå等字符的文件。如果我们假设我已将文本文件的一行存储如下:

#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”,应返回5而不是8

我需要以另一种方式读取文件吗?我不应该使用char*而应该使用int*吗?

3个回答

3

假设您使用UTF-8。

您需要了解UTF-8的工作原理

以下是一小段代码,应该可以满足您的需求:

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;
}

它返回字符数,如果不是有效的UTF-8字符串,则返回-1。
(通过非有效的UTF-8字符串,我指的是格式无效。我不检查字符是否实际存在)
编辑:如评论部分所述,此代码不处理分解的Unicode。

2
这是为什么你真的需要使用库的好例子。上面的代码不一定会正确计算字符数,因为某些字符可以用多种方式编码,例如å可能以UTF-8中的单个字符C3 A5编码,也可能被编码为a后跟˚,即61 CB 9A。这两种形式分别称为组合和分解Unicode。 - JeremyP

2

标准C IO库只能读取字节。你的文件可能包含使用UTF8或其他编码方式编码的多字节字符。你需要使用一个解释这种文件的库。

也有可能你的文件包含Latin1文本,其中字符就是字节。在这种情况下,除非设置了正确的语言环境,否则无法使用isgraph函数。

总之:找出文件所使用的编码方式,然后相应地进行读取。无论如何,纯粹的C语言不知道编码方式。


1
请参考 https://dev59.com/ZlPTa4cB1Zd3GeqPgBaN。 - lhf

2
你需要了解哪种编码被用于你的字符。我猜很可能是 UTF-8(并且你应该在所有地方使用 UTF8……),阅读Joel关于Unicode的博客。如果你的编码不是UTF-8,你应该将其转换为UTF-8,例如使用libiconv
那么您需要一个用于UTF-8的C库。有许多这样的库(但目前还没有在C11语言中标准化)。我推荐使用libunistringglib(来自GTK),但也可以参考this
您的代码会发生变化,因为UTF-8字符可以占用1到4个[8位]字节(但维基百科 UTF-8页面提到最多6个字节;有关详细信息,请参见 Unicode标准)。 您不会测试一个字节(即一个普通的C char )是否是字母,而是测试一个字节及其后面的几个字节(由指针给出,即 char * 或更好的 uint8_t * )是否编码一个字母(包括西里尔字母等)。
并非每个字节序列都是有效的UTF-8表示形式,您可能希望在分析它之前验证一行(或以null结尾的C字符串)。

2
在UTF-8中表示Unicode代码点所需的最大字节数为4个,无论早期文档建议什么。最后一个Unicode值是U+10FFFF。十年甚至更久以前,上限未被定义。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接