当前位置: 首页 > 工具软件 > UTF-8 CPP > 使用案例 >

C++ 读取 UTF8 文件

卓致远
2023-12-01

C++ 读取 UTF8 文件,转换为字符序列:

#include <iostream>
#include <fstream>
#include <vector>

// UTF8 编码格式(xxx 是用来填充二进制 Unicode 码点的)
//
// 1字节	0xxxxxxx 
// 2字节	110xxxxx_10xxxxxx 
// 3字节	1110xxxx_10xxxxxx_10xxxxxx
// 4字节	11110xxx_10xxxxxx_10xxxxxx_10xxxxxx
// 5字节	111110xx_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx
// 6字节	1111110x_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx
//
// 有效的 Unicode 码点范围为 0-0x10FFFF,最多用到 4 字节 UTF8 编码

using namespace std;

// 读取 UTF8 文件
vector<uint32_t> read_utf8_file(string filename) {
	// 打开文件
	ifstream f(filename);
	if (!f.is_open()) {
		perror("ifstream -> open()");
		exit(1);
	}

	// 跳过 UTF8 BOM(0xEFBBBF)
	if (f.get() != 0xEF || f.get() != 0xBB || f.get() != 0xBF) {
		f.seekg(0, ios::beg);
	}

	unsigned char c;    // UTF8 码点,涉及位运算,必须使用无符号数
	uint32_t w;         // Unicode 码点
	vector<uint32_t> v; // 用于存储转换结果的 Unicode 码点序列

	int len;	        // 单个 UTF8 字符的编码长度

	while ((c = f.get()) && !f.eof()) {
		if (c < 0b10000000) {
			// 单字节编码
			w = c;
		} else {
			// 多字节编码,获取编码长度
			if (c > 0b11110100) {
				cout << (uint32_t)c << endl;
				// 超出可用 Unicode 范围 0x10FFFF
				// 11110100_10001111_10111111_10111111
				fprintf(stderr, "Invalid unicode range\n");
				exit(1);
			} else if (c >= 0b11110000) {
				len = 4;
			} else if (c >= 0b11100000) {
				len = 3;
			} else if (c >= 0b11000000) {
				len = 2;
			} else {
				// 首字节不能小于 0b11000000
				fprintf(stderr, "Invalid utf8 leading code");
				exit(1);
			}
			// 通过左移再右移的方法去掉首字节中的 UTF8 标记
			c = c << (len + 1);
			w = c >> (len + 1);

			// 处理后续 UTF8 编码
			while(len > 1) {
				c = f.get();
				// 如果 f 到达 eof,则 c 会返回 255,刚好匹配下面的错误检查
				// 后续编码必须是 0b10xxxxxx 格式
				if (c >= 0b11000000) {
					fprintf(stderr, "Invalid utf8 tailing code");
					exit(1);
				}
				len--;
				c = c & 0b00111111;  // 去掉 UTF8 标记
				w = w << 6;          // 腾出 6 个 bit 的位置
				w += c;              // 将去掉了 UTF8 标记的编码合并进来
			}
		}
		v.push_back(w);     // 存储解解析结果
	}
	return v;
}

int main() {
	vector<uint32_t> v;
	uint32_t w;

	v = read_utf8_file("test1.txt");

	for (w : v) {
		cout << w << ' ';
	};
	cout << endl;
	return 0;
}
 类似资料: