C++实现ANSI编码转换为UTF-8编码格式文件

文章结构:本文的先介绍了常见的几种编码格式:ANSI,Unicode,UTF-8,在进行编码转换之前,需要先判断文件的编码格式,在编码转换完成之后,需要将文件保存为UTF-8编码格式的文件。

文本文件编码格式介绍

在计算机内部,所有的数据都是以二进制的形式存储的。在存储文本时,需要把文本信息转换为二进制进行保存,而在显示时则需要把二进制转换为文本信息显示出来。编码就是二进制与显示的字符之间转行的规则。

ASCII

最开始是美国制定了一套字符编码,主要用来显示英文字符,称为ASCII(美国信息交换标准码),用一个字节来表示一个字符。

GBK

ASCII编码只适合用来显示英文字符,但是对于像中文这样6000多个常用汉字的语言来说,一个字节的大小完全不够用,所以中国就制定了一套自己的编码格式,每个汉字及符号使用两个字节表示。最开始是在1981年发布的GBK2312标准,后来又制定了GBK标准。

GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年10月制定, 1995年12月正式发布,目前中文版的WIN95、WIN98、WINDOWS NT以及WINDOWS 2000、WINDOWS XP、WIN 7等都支持GBK编码方案。

ASNI

除了中国,其他许多国家也都制定了自己的编码标准,如日文的Shift-JS编码。为了使计算机支持多种语言,使用0x00~0x7F范围的一个字节表示一个英文字符,超出此范围的根据不同操作系统的语言进行编码,如在简体中文的操作系统中ASNI表示GBK编码,在日文操作系统中,ASNI表示Shift-JS编码。不同的ASNI编码之间互不兼容。

Unicode

由于不同的ANSI编码之间互不兼容,如果不容地区之间需要进行信息交互,就会经常需要进行编码转换,非常不方便。为了解决这个问题,ISO(国际标准化组织)制定了可以包含所有文字,符号的编码Unicode,Unicode规定用两个字节来统一表示所有字符,总共可以组合65532个字符。但是Unicode在制定的时候并没有考虑与其他编码格式兼容的问题,只能通过查表的方式来完成转换。

UTF-8

Unicode可以表示所有的字符,但是英文字符也与其他字符一样,使用两个字节进行编码,使得在保存英文文本的时候会多出一倍的存储空间,而大多数的文本信息都是英文的。尤其是在网络传输的时候,即使看到的页面的中文的网页,背后的代码也大多数的英文的。为了节省空间,提出一种可变长的编码方式UTF-8。

UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。

虽然部分字符的表示UTF-8编码方式需要比Unicode编码方式的表示需要更多的字符, 但是英文却只需要一个字符,对于大部分文本信息是英文的文件来说, UTF-8的编码格式能够节省大量的空间。但是如果大部分的文本信息是中文的话,可能使用GBK编码更节省空间。
现在采用最多的编码方式就是UTF-8的编码方式。

检测文件编码

在转换文本文件的编码之前,需要先检测文件的编码,再进行转换。对于文本文件,需要判断的编码有:ANSI,Unicode,Unicode big endian, UTF-8 with BOM, UTF-8 without BOM。
其中,Unicode,Unicode big endian和UTF-8 with BOM的文件在文件头部有两个字节用来判断是什么编码格式,ANSI和UTF-8 with BOM文件则没有,需要对文本进行采样,统计分析后判断是哪一种编码。网上搜索说可以使用uchardet这个库来进行检测,但是本文是直接读取整个文件然后判断编码格式的。

text_encode check_text_encode(const string &file_name) {
	ifstream file_in(file_name, ios::binary);

	if (!file_in.is_open()) {
		cout << "打开文件失败" << endl;;
		system("pause");
		return UNKNOW;
	}

	int head;
	unsigned char ch;
	file_in.read((char*)&ch, sizeof(ch));
	head = ch << 8;
	file_in.read((char*)&ch, sizeof(ch));
	head |= ch;

	file_in.close();
	text_encode result_code;
	switch (head) {
		case 0xFFFE:
			result_code = UNICODE;
			break;
		case 0xFEFF:
			result_code = UNICODE_BIG_ENDIAN;
			break;
		case 0xEFBB:
			result_code = UTF8WITHOUTBOM;
			break;
		default:
			if (check_utf8_without_bom(file_name))
				result_code = UTF8;
			else
				result_code = ANSI;
			break;
	}
	return result_code;
}

通过读取文件头检测文本文件的编码参考博客:https://blog.csdn.net/bladeandmaster88/article/details/54767557
没有BOM头的UTF-8编码格式的文件没有使用第三方库,而是直接读取整个文件进行判断,也是参考了网上的文章,具体忘记在哪里看的了…

bool check_utf8_without_bom(const string &file_name) {

	ifstream file_in;
	//判断文件编码
	file_in.open(file_name, ios::in);

	if (!file_in.is_open()) {
		cout << "打开文件失败" << endl;;
		system("pause");
		return false;
	}

	stringstream buffer;
	buffer << file_in.rdbuf();
	file_in.close();
	string text = buffer.str();

	int len = text.size();
	int n = 0;
	unsigned char ch;
	bool b_all_ascii = true;

	for (size_t i = 0; i < len; ++i) {
		ch = text[i];

		if ((ch & 0x80) != 0) {
			b_all_ascii = false;
		}

		if (n == 0) {
			if (ch >= 0x80) {
				if (ch >= 0xFC && ch <= 0xFD) {
					n = 6;
				} else if (ch >= 0xF8) {
					n = 5;
				} else if (ch >= 0xF0) {
					n = 4;
				} else if (ch >= 0xE0) {
					n = 3;
				} else if (ch >= 0xC0) {
					n = 2;
				} else {
					return false;
				}

				n--;
			}
		} else {
			if ((ch & 0xC0) != 0x80) {
				return false;
			}

			n--;
		}
	}

	if (n > 0) {
		return false;
	}

	if (b_all_ascii) {
		return false;
	}

	return true;
}

编码转换与保存

ANSI编码可以转换为Unicode,Unicode可以转换为UTF-8,但是ANSI没办法直接转换为UTF-8,可以先将ANSI转换为Unicode,再将Unicode转换为UTF-8。
C++11标准库提供了方便转换编码的函数MultiByteToWideChar
将ANSI编码转换为Unicode

wstring ANSI2Unicode(const string &str) {
	int len = str.size();
	int unicode_len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
	wchar_t *unicode_p = new wchar_t[unicode_len + 1];
	memset(unicode_p, 0, (unicode_len) * sizeof(wchar_t));
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, (LPWSTR)unicode_p, unicode_len);
	wstring str_w;
	str_w = (wchar_t *)unicode_p;
	delete unicode_p;
	return str_w;
}

再将Unicode编码的文本保存为UTF-8编码的文本文件,

void save_as_utf8(string file_name, string content) {
	wstring content_unicode = ANSI2Unicode(content);
	wofstream ofs(file_name, ios::ate);
	//std::generate_header表示带BOM的UTF-8,std::little_endian表示不带BOM的UTF-8
	//ofs.imbue(std::locale(ofs.getloc(), new std::codecvt_utf8<wchar_t, 0x10ffff, std::generate_header>));
	ofs.imbue(std::locale(ofs.getloc(), new std::codecvt_utf8<wchar_t, 0x10ffff, std::little_endian>));
	ofs << content_unicode;
	ofs.close();

}

更多的编码转换和保存可以参考博客https://blog.csdn.net/u010383605/article/details/79946049

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值