C语言函数:修复UTF-8字符串截断问题,并返回完整中文内容
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,因此需要设置本地化环境。
原文地址: https://www.cveoy.top/t/topic/mJ3Z 著作权归作者所有。请勿转载和采集!