手把手教你实现字符串编码转换系统

2023-12-28 10:39:44 浏览数 (2)

写在前面

字符集是对特定语言中所有可读或可显示字符的称呼。例如英语、汉语、日语等都是不同的字符集。字符集决定了可以展示和表示的字符范围。在字符集中,需要使用编码字符集来实现字符的编码和转码。编码字符集使用编码值来表示字符在字库表中的位置。字库表是一个包含了所有可读或可显示字符的数据库,它决定了字符集能够展示的所有字符的范围。字符编码定义了编码字符集和实际存储数值之间的转换关系。常见的字符编码方式包括ASCII、ISO 8859-1、GB2312、GBK等。常情况下,一个字符集对应一个编码方式,比如ASCII、ISO 8859-1、GB2312、GBK等都是针对特定字符集的编码方式。

然而,一个字符集也可以有多种编码方式。例如,UCS字符集(也是Unicode使用的字符集)上有UTF-8、UTF-16、UTF-32等编码方式。

  • UTF8:又分为带签名和不带签名两种,Windows代码页为65001,VS中应该选择【UTF8-带签名】的格式
  • GBK/GB2312:Windows代码页为936
  • GB18030: Windows代码页为54936

具体实现小demo

iconv

代码语言:c复制
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>

using namespace std;

#ifdef _WIN32
#include <windows.h>

string GbkToUtf8(const char *src_str)
{
	int len = MultiByteToWideChar(CP_ACP, 0, src_str, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len   1];
	memset(wstr, 0, len   1);
	MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len   1];
	memset(str, 0, len   1);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	string strTemp = str;
	if (wstr) delete[] wstr;
	if (str) delete[] str;
	return strTemp;
}

string Utf8ToGbk(const char *src_str)
{
	int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, NULL, 0);
	wchar_t* wszGBK = new wchar_t[len   1];
	memset(wszGBK, 0, len * 2   2);
	MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wszGBK, len);
	len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
	char* szGBK = new char[len   1];
	memset(szGBK, 0, len   1);
	WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
	string strTemp(szGBK);
	if (wszGBK) delete[] wszGBK;
	if (szGBK) delete[] szGBK;
	return strTemp;
}
int GbkToUtf8(char *str_str, size_t src_len, char *dst_str, size_t dst_len)
{
	iconv_t cd;
	char **pin = &str_str;
	char **pout = &dst_str;

	cd = iconv_open("utf8", "gbk");
	if (cd == 0)
		return -1;
	memset(dst_str, 0, dst_len);
	if (iconv(cd, pin, &src_len, pout, &dst_len) == -1)
		return -1;
	iconv_close(cd);
	*pout = '';

	return 0;
}

int Utf8ToGbk(char *src_str, size_t src_len, char *dst_str, size_t dst_len)
{
	iconv_t cd;
	char **pin = &src_str;
	char **pout = &dst_str;

	cd = iconv_open("gbk", "utf8");
	if (cd == 0)
		return -1;
	memset(dst_str, 0, dst_len);
	if (iconv(cd, pin, &src_len, pout, &dst_len) == -1)
		return -1;
	iconv_close(cd);
	*pout = '';

	return 0;
}


int main(void)
{
	char *src_str = "您好世界,hello";
	cout << "origin string: " << src_str << endl;
	// windows default is gbk
	string dst_str = GbkToUtf8(src_str);
	cout << "gbk to utf8: " << dst_str << endl;

	string str_utf8 = Utf8ToGbk(dst_str.c_str());
	cout << "utf8 to gbk: " << str_utf8 << endl;
	// unix default is utf8
	char dst_gbk[1024] = {0};
	Utf8ToGbk(src_str, strlen(src_str), dst_gbk, sizeof(dst_gbk));
	cout << "utf8 to gbk: " << dst_gbk << endl;

	char dst_utf8[1024] = {0};
	GbkToUtf8(dst_gbk, strlen(dst_gbk), dst_utf8, sizeof(dst_utf8));
	cout << "gbk to utf8: " << dst_utf8 << endl;
	return 0;
}


  1. GbkToUtf8 函数和 Utf8ToGbk 函数分别实现了在 Windows 平台下的 GBK 转 UTF-8 和 UTF-8 转 GBK 的功能。
  2. GbkToUtf8 函数使用了 Windows API 中的 MultiByteToWideCharWideCharToMultiByte 来进行编码的转换。
  3. Utf8ToGbk 函数同样使用了 Windows API 中的 MultiByteToWideCharWideCharToMultiByte 来进行编码的转换。
  4. GbkToUtf8 函数的另一种实现方式是使用了 iconv 函数,可以实现不同平台下的编码转换。
  5. Utf8ToGbk 函数也使用了 iconv 函数进行编码转换。

关键函数解读

Utf8ToGbk 函数接受四个参数,分别是源字符串指针 src_str、源字符串长度 src_len、目标字符串指针 dst_str 以及目标字符串长度 dst_len

首先定义了一个 iconv_t 类型的变量 cd,用于表示一个字符转换的描述符。

通过 iconv_open("gbk", "utf8") 打开一个从 UTF-8 到 GBK 的转换描述符,并将其赋值给 cd。如果打开失败,返回 -1。

使用 iconv 函数进行实际的编码转换,将源字符串的内容从 UTF-8 转换为 GBK,并将结果存储到目标字符串中。

在转换之前,先使用 memset 将目标字符串清零,避免之前可能存在的脏数据影响转换结果。

如果转换过程中出现错误,iconv 函数会返回 -1,并且函数也会返回 -1。

最后使用 iconv_close 关闭转换描述符,并将目标字符串末尾设置为 '',表示字符串结束。

如果转换顺利完成,则函数返回 0,表示转换成功。

iconv_open

函数原型为:

代码语言:c复制
iconv_t iconv_open (const char* tocode, const char* fromcode);

这个函数返回一个把formcode编码的字符串转换成tocode编码的字符串的转换描述符。如果发生错误返回(iconv_t)-1,同时设置errno。

iconv

函数原型为:

代码语言:c复制
       size_t iconv(iconv_t cd,
                    char **inbuf, size_t *inbytesleft,
                    char **outbuf, size_t *outbytesleft);

这个函数利用iconv_open返回的转换描述符执行实际的转换操作。inbuf代表需要转换编码的字符地址,inbytesleft代表需要转换的字符个数地址,outbuf代表需要转换后的字符存放地址,outbytesleft代表存放转换后字符的最大个数地址。这个函数会修改传进来的参数,所以,要用另外用四个变量来表示这个地址。

函数执行失败返回(size_t)-1,errno被设置,否则,返回以不可逆的方式转换的字符个数,可逆转换的字符个数没有统计。

iconv_close

函数原型为:

代码语言:c复制
       int iconv_close (iconv_t cd);

这个函数关闭之前调用iconv_open返回的描述符,释放内存空间,如果成功返回0,否则返回-1,errno被设置。

0 人点赞