c读取非ASCII字符

时间:2015-09-11 12:47:45

标签: c text character-encoding 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”将返回5而不是8

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

3 个答案:

答案 0 :(得分:2)

C标准IO库只能读取字节。您的文件可能包含多字节字符,使用UTF8或其他编码进行编码。您需要一个库来解释此类文件。

您的文件可能包含Latin1文本,在这种情况下,字符是字节。在这种情况下,除非您设置了正确的区域设置,否则无法使用isgraph

底线:查找文件中使用的编码。然后相应地阅读。在任何情况下,普通C都不知道编码。

答案 1 :(得分:2)

您需要了解哪些编码用于您的角色。我想这很可能UTF-8(你应该使用UTF8 everywhere ....),阅读Joel's blog on Unicode。如果您的编码不是UTF-8,则应将其转换为UTF-8,例如:使用libiconv

然后你需要一个用于UTF-8的C库。它们中有很多(但是C11语言中没有一个是标准化的)。我建议使用libunistringglib(来自GTK),但另请参阅this

您的代码将会更改,因为UTF-8字符可能需要一到四[8位]字节(但Wikipedia UTF-8页面最多提到6个字节;有关详细信息,请参阅Unicode标准。你不会测试一个字节(即普通的C char)是否是一个字母,而是一个字节和它后面的几个字节(由指针给出,即char*或更好的{ {1}})编码一个字母(包括西里尔字母等)。

并非每个字节序列都是有效的UTF-8表示,并且您可能希望在分析之前验证一行(或以空值终止的C字符串)。

答案 2 :(得分:1)

我们假设您使用的是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;
}

它返回字符数,如果它不是有效的UTF-8字符串,则返回-1。

(通过无效的UTF-8字符串,我的意思是格式无效。我不检查字符是否确实存在)

编辑:如评论部分所述,此代码不处理分解的unicode