C语言函数:修复UTF-8字符串截断问题,并返回完整中文内容

本函数用于判断一个字符串是否为UTF-8截断的,如果是中文截断,则往前取一个完整的中文内容。函数接收一个UTF-8编码的字符串作为输入,并返回一个修复后的宽字符字符串,如果输入字符串不合法,则返回NULL。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>

// 判断一个字节是否为UTF-8的leading byte
int is_utf8_leading_byte(unsigned char c) {
    return (c & 0b10000000) == 0b11000000 || (c & 0b11100000) == 0b11100000;
}

// 判断一个字节是否为UTF-8的continuation byte
int is_utf8_continuation_byte(unsigned char c) {
    return (c & 0b11000000) == 0b10000000;
}

// 判断一个字节序列是否为有效的UTF-8编码
int is_valid_utf8(const unsigned char *s, size_t len) {
    size_t i = 0;
    while (i < len) {
        if ((s[i] & 0b10000000) == 0) {  // ASCII字符
            i++;
        } else if ((s[i] & 0b11100000) == 0b11000000) {  // 2字节UTF-8字符
            if (i + 1 >= len || !is_utf8_continuation_byte(s[i+1])) {
                return 0;
            }
            i += 2;
        } else if ((s[i] & 0b11110000) == 0b11100000) {  // 3字节UTF-8字符
            if (i + 2 >= len || !is_utf8_continuation_byte(s[i+1]) || !is_utf8_continuation_byte(s[i+2])) {
                return 0;
            }
            i += 3;
        } else if ((s[i] & 0b11111000) == 0b11110000) {  // 4字节UTF-8字符
            if (i + 3 >= len || !is_utf8_continuation_byte(s[i+1]) || !is_utf8_continuation_byte(s[i+2]) || !is_utf8_continuation_byte(s[i+3])) {
                return 0;
            }
            i += 4;
        } else {  // 非法的UTF-8字符
            return 0;
        }
    }
    return 1;
}

// 查找一个UTF-8字符的前一个leading byte
const unsigned char *find_utf8_prev_leading_byte(const unsigned char *s, const unsigned char *p) {
    while (p > s && is_utf8_continuation_byte(*--p)) {}
    return p;
}

// 判断一个字符串是否为UTF-8截断的,如果是中文截断往前取一个完整的中文字节序列
wchar_t *fix_truncated_utf8(const char *s) {
    size_t len = strlen(s);
    if (!is_valid_utf8((const unsigned char *)s, len)) {
        return NULL;
    }
    wchar_t *ws = (wchar_t *)malloc((len+1) * sizeof(wchar_t));
    if (ws == NULL) {
        return NULL;
    }
    size_t i = 0, j = 0;
    while (i < len) {
        const unsigned char *p = (const unsigned char *)&s[i];
        if (!is_utf8_leading_byte(*p)) {  // 非leading byte
            p = find_utf8_prev_leading_byte((const unsigned char *)s, p);
            i = p - (const unsigned char *)s + 1;
        } else if (i + 3 < len && (p[0] & 0b11111000) == 0b11100000 && is_utf8_continuation_byte(p[1]) && is_utf8_continuation_byte(p[2])) {  // 3字节中文字符
            ws[j++] = (wchar_t)((p[0] & 0b00000111) << 12 | (p[1] & 0b00111111) << 6 | (p[2] & 0b00111111));
            i += 3;
        } else if (i + 2 < len && (p[0] & 0b11100000) == 0b11000000 && is_utf8_continuation_byte(p[1])) {  // 2字节中文字符
            ws[j++] = (wchar_t)((p[0] & 0b00011111) << 6 | (p[1] & 0b00111111));
            i += 2;
        } else {  // 其他情况
            ws[j++] = (wchar_t)(unsigned char)s[i++];
        }
    }
    ws[j] = L'\0';
    return ws;
}

int main() {
    setlocale(LC_ALL, "");  // 设置本地化环境,以便支持宽字符输出
    const char *s = "Hello, 世界";
    wchar_t *ws = fix_truncated_utf8(s);
    if (ws != NULL) {
        wprintf(L"%ls\n", ws);
        free(ws);
    }
    return 0;
}

该函数的基本思路是,遍历输入字符串的每个字符,判断是否为UTF-8的leading byte,如果是则判断该字符是否为中文字符(3字节或2字节),如果是则往前找到一个完整的中文字节序列,将其转换为宽字符。如果不是leading byte,则往前找到一个leading byte,重新开始判断。如果遇到非法的UTF-8字符,则返回NULL。

注意,该函数使用了C99标准的定长整数类型(如uint8_t)和二进制字面量(如0b10000000),如果编译器不支持这些特性,需要做相应的修改。此外,该函数需要用到宽字符输出函数wprintf,因此需要设置本地化环境。

C语言函数:修复UTF-8字符串截断问题,并返回完整中文内容

原文地址: https://www.cveoy.top/t/topic/mJ3Z 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录