使用 C++ 方式实现 GBK 到 UTF-8 转码 (win / linux)

文章介绍了三种在C++中实现GBK到UTF-8编码转换的方法:使用QtAPI,利用std::codecvt进行Unicode中转,以及在Windows上使用WinAPI和在Linux上使用iconv库。对于std::codecvt,文章提供了详细的转换函数实现,而WinAPI和iconv则针对不同操作系统提供了相应的转换接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++ 存在多种方式实现 GBK 到 UTF-8 的转码

1 - 使用 Qt API


一般使用C++都会想到使用 Qt API QString 将 gbk 转为 utf-8

std::string sgbk;
std::string sutf8 = QString::fromLocal8Bit(sgbk.data()).toUtf8().data());

此种方式,可以转换 Windows 平台运行时的 gbk 编码的中文字符串为 utf-8 格式,linux 下需要使用 QTextCodec ,网上有很多,此处不做过多描述。
由于项目需要去掉 Qt 依赖,或者无法使用 Qt,所有给出以下两种方法。

2 - 使用 std::codecvt


C++标准库封装了部分转码方法,需要通过 unicode 中转,调用 <codecvt> 与 <clocale> 来实现。

首先实现四个基础方法

class String {
private:
	static std::string UnicodeToUtf8(const std::wstring& wstr);
	static std::wstring Utf8ToUnicode(const std::string& str);
	static std::string UnicodeToAnsi(const std::wstring& wstr);
	static std::wstring AnsiToUnicode(const std::string& str);
};

unicode 与 utf-8 之间的相互转换

std::string String::UnicodeToUtf8(const std::wstring& wstr)
{
	std::string out;
	try {
		std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
		out = wcv.to_bytes(wstr);
	}
	catch (const std::exception & e)
	{
		std::cerr << e.what() << std::endl;
	}
	return out;
}

std::wstring String::Utf8ToUnicode(const std::string& str)
{
	std::wstring ret;
	try
	{
		std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
		ret = wcv.from_bytes(str);
	}
	catch (const std::exception & e)
	{
		std::cerr << e.what() << std::endl;
	}
	return ret;
}

然后实现 unicode 与 ansi 之间的转换

std::string String::UnicodeToAnsi(const std::wstring& wstr)
{
	std::string ret;
	std::mbstate_t state{};
	const wchar_t* src = wstr.data();
	size_t len = std::wcsrtombs(nullptr, &src, 0, &state);

	if (len != static_cast<size_t>(-1))
	{
		std::unique_ptr<char[]> buff(new char[len + 1]);
		len = std::wcsrtombs(buff.get(), &src, len, &state);
		if (len != static_cast<size_t>(-1))
		{
			ret.assign(buff.get(), len);
		}
	}
	return ret;
}

std::wstring String::AnsiToUnicode(const std::string& str)
{
	std::wstring ret;
	std::mbstate_t state{};
	const char* src = str.data();
	size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
	if (len != static_cast<size_t>(-1))
	{
		std::unique_ptr<wchar_t[]> buff(new wchar_t[len + 1]);
		len = std::mbsrtowcs(buff.get(), &src, len, &state);
		if (len != static_cast<size_t>(-1))
		{
			ret.assign(buff.get(), len);
		}
	}
	return ret;
}

然后实现最外层的方法

class String {
public:
	static std::string Utf8ToAnsi(const std::string& str);
	static std::string AnsiToUtf8(const std::string& str);
};

只需要级联调用即可

std::string String::Utf8ToAnsi(const std::string& str)
{
	return UnicodeToAnsi(Utf8ToUnicode(str));
}

std::string String::AnsiToUtf8(const std::string& str)
{
	return UnicodeToUtf8(AnsiToUnicode(str));
}

主函数入口处需设置 setlocale,此处调用为了使 ANSI 编码生效,由于 ANSI 在不同平台下表示的编码不同。

// to let ANSI take effects to enable AnsiToUtf8
setlocale(LC_CTYPE, "");

完整代码 String.h,实现一个纯接口类

#pragma once
#include <string>
class String
{
public:
	// 对外接口
	static std::string Utf8ToAnsi(const std::string& str);
	static std::string AnsiToUtf8(const std::string& str);

private:
	// 内部调用
	static std::string UnicodeToUtf8(const std::wstring& wstr);
	static std::wstring Utf8ToUnicode(const std::string& str);
	static std::string UnicodeToAnsi(const std::wstring& wstr);
	static std::wstring AnsiToUnicode(const std::string& str);
	
	// disabled functions
	String() = delete;
	~String() = delete;
	String(const String& rhs) = delete;
	String& operator=(const String& rhs) = delete;
};

#pragma once 现在较新版本的编译器一般都支持,如果不支持需要换成
#ifndef __STRING_H__
#define __STRING_H__
#endif // __STRING_H__
防止头文件重复包含

String.cpp

#include "String.h"
#include <codecvt>
#include <iostream>

std::string String::Utf8ToAnsi(const std::string& str)
{
	return UnicodeToAnsi(Utf8ToUnicode(str));
}

std::string String::AnsiToUtf8(const std::string& str)
{
	return UnicodeToUtf8(AnsiToUnicode(str));
}

std::string String::UnicodeToUtf8(const std::wstring& wstr)
{
	std::string out;
	try {
		std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
		out = wcv.to_bytes(wstr);
	}
	catch (const std::exception & e)
	{
		std::cerr << e.what() << std::endl;
	}
	return out;
}

std::wstring String::Utf8ToUnicode(const std::string& str)
{
	std::wstring ret;
	try
	{
		std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
		ret = wcv.from_bytes(str);
	}
	catch (const std::exception & e)
	{
		std::cerr << e.what() << std::endl;
	}
	return ret;
}

std::string String::UnicodeToAnsi(const std::wstring& wstr)
{
	std::string ret;
	std::mbstate_t state{};
	const wchar_t* src = wstr.data();
	size_t len = std::wcsrtombs(nullptr, &src, 0, &state);

	if (len != static_cast<size_t>(-1))
	{
		std::unique_ptr<char[]> buff(new char[len + 1]);
		len = std::wcsrtombs(buff.get(), &src, len, &state);
		if (len != static_cast<size_t>(-1))
		{
			ret.assign(buff.get(), len);
		}
	}
	return ret;
}

std::wstring String::AnsiToUnicode(const std::string& str)
{
	std::wstring ret;
	std::mbstate_t state{};
	const char* src = str.data();
	size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
	if (len != static_cast<size_t>(-1))
	{
		std::unique_ptr<wchar_t[]> buff(new wchar_t[len + 1]);
		len = std::mbsrtowcs(buff.get(), &src, len, &state);
		if (len != static_cast<size_t>(-1))
		{
			ret.assign(buff.get(), len);
		}
	}
	return ret;
}

由于 setlocale 会影响全局,也就是所有的 lib 库都会影响,有可能会出现问题,所以建议第三种方法。

3 - 使用 WinAPI 和 iconv


Windows 平台使用 Win API, linux 平台使用 iconv 库

首先实现一个 linux 下的通用函数,由于直接使用 std::string 和 iconv 接口会出现转换失败的问题。

// 根据不同的平台包含不同的头文件
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64) 
#include <Windows.h>
#elif defined(__linux__) || defined(__GNUC__)
#include <iconv.h>
#endif

#if defined(__linux__) || defined(__GNUC__)
int EncodingConvert(const char* charsetSrc, const char* charsetDest, char* inbuf,
	size_t inSz, char* outbuf, size_t outSz)
{
	iconv_t cd;
	char** pin = &inbuf;
	char** pout = &outbuf;
	cd = iconv_open(charsetDest, charsetSrc);
	if (0 == cd)
	{
		std::cerr << charsetSrc << " to " << charsetDest 
		<< " conversion not available" << std::endl; 
		return -1;
	}

	if (-1 == static_cast<int>(iconv(cd, pin, &inSz, pout, &outSz)))
	{
		std::cerr << "conversion failure" << std::endl;
		return -1;
	}

	iconv_close(cd);
	**pout = '\0';
	return 0;
}
#endif

实现 GBK 转 UTF-8 的接口,设置转换失败和非 Windows 和非 Linux 系统,返回原字符串。

std::string GbkToUtf8(const std::string& str)
{
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64)
	int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1ull];
	memset(wstr, 0, len + 1ull);
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* cstr = new char[len + 1ull];
	memset(cstr, 0, len + 1ull);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, cstr, len, NULL, NULL);
	std::string res(cstr);

	if (wstr) delete[] wstr;
	if (cstr) delete[] cstr;

	return res;
#elif defined(__linux__) || defined(__GNUC__)
	size_t len = str.size() * 2 + 1;
	char* temp = new char[len];
	if (EncodingConvert("gb2312", "utf-8", const_cast<char*>(str.c_str()), str.size(), temp, len)
		> = 0)
	{
		std::string res;
		res.append(temp);
		delete[] temp;
		return res;
	}
	else
	{
		delete[]temp;
		return str;
	}
#else
	std::cerr << "Unhandled operating system." << std::endl;
	return str;
#endif
}

实现 UTF-8 转 GBK 的接口,与前者一样,非 Windows 和非 Linux 系统未处理和处理失败返回原字符串

std::string Utf8ToGbk(const std::string& str)
{
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64) 
    // calculate length
	int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
	wchar_t* wsGbk = new wchar_t[len + 1ull];
	// set to '\0'
	memset(wsGbk, 0, len + 1ull);
	MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wsGbk, len);
	len = WideCharToMultiByte(CP_ACP, 0, wsGbk, -1, NULL, 0, NULL, NULL);
	char* csGbk = new char[len + 1ull];
	memset(csGbk, 0, len + 1ull);
	WideCharToMultiByte(CP_ACP, 0, wsGbk, -1, csGbk, len, NULL, NULL);
	std::string res(csGbk);

	if (wsGbk)
	{
	 	delete[] wsGbk;
	}
	
	if (csGbk)
	{
		delete[] csGbk;
	}

	return res;
#elif defined(__linux__) || defined(__GNUC__)
	size_t len = str.size() * 2 + 1;
	char* temp = new char[len];
	if (EncodingConvert("utf-8", "gb2312", const_cast<char*>(str.c_str()),
		str.size(), temp, len) >= 0)
	{
		std::string res;
		res.append(temp);
		delete[] temp;
		return res;
	}
	else
	{
		delete[] temp;
		return str;
	}

#else
	std::cerr << "Unhandled operating system." << std::endl;
	return str;
#endif
}

GBK 转 UTF-8 两个平台均验证测试可行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值