首页 > linux C 语言如何遍历 utf-8字符

linux C 语言如何遍历 utf-8字符

我的需求是从utf-8文件读入到内存,然后想遍历它来寻找某个特殊字符。

我的代码片段:

// 检查utf-8字符所占字节数
#define UTF8_CHAR_LEN( byte ) ((( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1)
FILE *fp = fopen(PATH_TO_FILE, "r");

char *buf = malloc(LENGTH_OF_FILE * sizeof(char));

fgets(buf, LENGTH_OF_FILE, fp);

int step = 0;
for (; *(buf + step) != '\0'; ++step) {
  int utf8_word_len = UTF8_CHAR_LEN(buf + step);
  // utf8是多字节的,我也确定了编码是utf-8 所以不需要判断是否是其他的编码了
  if (utf8_word_len > 1) {
    char word[utf8_word_len];
    strncpy(word, (buf+step), urf8_word_len);
    step += urf8_word_len;
    // 这里就可以输出中文了,但是很奇怪啊,输出中文字符后还有一个很奇怪的字符
    // 肯定是我哪里搞错了,还请高手指点.
    printf("%s\n", word);
  }
  else {
    printf("%c\n", buf + step);
  }
}
fclose(fp);
free(buf);

问题是,当文件遇到utf-8编码的中文,这段代码就输出乱码了。我知道%c 是输出一个字节的内容,utf-8里面中文可能需要3个字节,所以有乱码会;但是自己不知道怎么处理 utf-8字符的遍历。

求高手解答.

PS: 我是想从字符指针去遍历utf-8的字符哦,不是想做编码转换的.


加入文件是utf-8编码,内容如下:

我是来自上海的互联网程序员,主要从事Web/游戏/手机服务器开发.

下面C代码可以一个一个的读入这些中文字符():

#define UTF8_CHAR_LEN( byte ) ((( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1)
char content[255]; //一行一行的读入文件,255的话大概满足需求了;这个代码片段也只是测试用.
FILE *data_file = fopen("path/to/file", "r");
if (!data_file) {
    fprintf(stderr, "error happened when read file: %s","path/to/file");
    return -1;
}
fgets(content, 255, data_file);
int i;
for (i = 0; content[i] != '\0'; i++) {
    int utf8_char_len = UTF8_CHAR_LEN(content[i]);
    if (utf8_char_len > 1) {
        char word[4]; // utf-8占位最大字节也就是4个字节
        memcpy(word, (content + i), utf8_char_len); 
        i += utf8_char_len - 1;
        printf("%s\t", word);
    }
    else {
        printf("%c\t", content[i]);
    }
}
fclose(data_file);

输入:

我 是 来 自 上 海 的 互 联 网 程 序 员 , 主 要 从事 W e b / 游 戏 / 手 机 服 务 器 开 发 .

这里我觉得主要问题是字符指针它不标识出编码字符的大小,如果单纯的用 (p + 1)的话会把编码混乱掉,所以要先判断字符编码的所占字节数,然后再 (p + sizeofword) 输出;

上面的代码满足我现在的需求,不过还有一些疑问;

utf-8编码的字符在Linux c 下面怎么处理的呢?一般模式我还在寻找,有机会帖在这里.

这里还帖一个用宽字符库遍历utf-8字符串的代码片段:

wchar_t wstr[] = L"你好";
    // en_US.UTF-8 是我本地的locale
setlocale(LC_ALL, "en_US.UTF-8");
int i;
for (i = 9; i < wcslen(wstr); i++) {
    wprintf(L"%lc\n", wstr[i]);
}

http://zh.wikipedia.org/zh-cn/UTF-8:
128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(Unicode范围由U+0080至U+07FF)。
其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。
其他极少使用的Unicode 辅助平面的字符使用四至六字节编码(Unicode范围由U+10000至U+1FFFFF使用四字节,Unicode范围由U+200000至U+3FFFFFF使用五字节,Unicode范围由U+4000000至U+7FFFFFFF使用六字节)。


如果可以使用C++的话 UTF8-CPP: UTF-8 with C++ in a Portable Way


你第一段代码应该是有两个问题,

1、step的自增有问题,不应该在每次for里面都++,而是根据字符宽度再加

2、printf打印是遇0结束,你的word最后一个字符根本不是0,所以就出现每个中文后面多出来的乱码,这个是很基础的问题。


使用 glib 的 utf8 相关函数,或者转成 wchar。或者拿 iconv 把编码转成 utf32 然后用 uint32_t 表示字符也成。

【热门文章】
【热门文章】