【字符编码转换】win32 api

        由于我准备换用iconv库来进行字符编码转换,所以记录一下windows平台MultiByteToWideCharWideCharToMultiByte的用法,以便以后万一用到。

//.h
namespace dnd
{
	/**
	  * @brief 字符串转换:utf-8 -> 宽字符
	*/
	std::wstring cvt_u8_wc(const string& str);

	/**
	  * @brief 字符串转换:utf-8 -> 本地编码
	  * @note	此函数只在必须时使用
	*/
	string cvt_u8_mb(const string& str);

	/**
	  * @brief 字符串转换:utf_string -> utf-8
	*/
	utf_string cvt_u8_utf(const string& str);

	//------------------wstring--------------------
	/**
	  * @brief 字符串转换:宽字符 -> utf-8
	*/
	string cvt_wc_u8(const wchar_t* str);

	/**
	  * @brief 字符串转换:宽字符 -> 本地编码
	*/
	string cvt_wc_mb(const wchar_t* str, CodePage code_page = CodePage::LOCAL);

	

	//------------------本地编码--------------------
	/**
	* @brief 字符串转换:mb -> utf-8
	*/
	string cvt_mb_u8(const char* str, CodePage code_page = CodePage::LOCAL);

	/**
	  * @brief 字符串转换:mb -> wc
	*/
	std::wstring cvt_mb_wc(const char* str, CodePage code_page = CodePage::LOCAL);

	//------------------未知utf--------------------
	/**
	  * @brief 字符串转换:utf_string -> utf-8
	*/
	string cvt_utf_u8(const utf_string& str);

	/**
	  * @brief 返回是否utf-8编码
	  * @retval 0 ascii
	  * @retval 1 utf-8
	  * @retval 2 mb
	*/
	natural is_u8(const char* str);

	/**
	  * @brief utf-8的安全复制
	  * @param[out] target 目标缓冲区
	  * @param[in] size 目标缓冲区大小
	  * @param[in] source 源字符串
	  * @param[in] log 是否输出日志,在源字符串过大时
	*/
	void cpy_u8(char* target, size_t size, const string& source, bool log = true);

	/**
	  * @brief utf-8的子串计算
	  * @param[in] str 源字符串
	  * @param[in] max 最大长度
	  * @retval 返回应该截断的位置
	*/
	size_t sub_u8(const string& str, size_t max);
}

//.cpp
namespace dnd
{

	natural String::is_u8(const char* str)
	{
		natural n_bytes = 0;//UFT8可用1-6个字节编码,ASCII用一个字节
		unsigned char ch;
		bool b_all_ascii = true; //如果全部都是ASCII, 说明不是UTF-8

		for (size_t i = 0; str[i]; ++i)
		{
			ch = *(str + i);
			if ((ch & 0x80) != 0) // 判断是否ASCII编码,如果不是,说明有可能是UTF-8,ASCII用7位编码,但用一个字节存,最高位标记为0,o0xxxxxxx
				b_all_ascii = false;
			if (n_bytes == 0) //如果不是ASCII码,应该是多字节符,计算字节数
			{
				if (ch >= 0x80)
				{
					if (ch >= 0xFC && ch <= 0xFD)
						n_bytes = 6;
					else if (ch >= 0xF8)
						n_bytes = 5;
					else if (ch >= 0xF0)
						n_bytes = 4;
					else if (ch >= 0xE0)
						n_bytes = 3;
					else if (ch >= 0xC0)
						n_bytes = 2;
					else
						return 2;

					--n_bytes;
				}
			}
			else //多字节符的非首字节,应为 10xxxxxx
			{
				if ((ch & 0xC0) != 0x80)
					return 2;

				--n_bytes;
			}
		}
		if (n_bytes > 0) //违返规则
			return 2;
		if (b_all_ascii) //如果全部都是ASCII, 说明不是UTF-8
			return 0;

		return 1;
	}

	std::wstring String::cvt_u8_wc(const string& str)
	{
		DWORD len = MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), -1, 0, 0);

		std::wstring ret;
		ret.resize(len);

		MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), -1, ret.data(), len);

		ret.pop_back();

		return ret;
	}

	string String::cvt_u8_mb(const string& str)
	{
		return cvt_wc_mb(cvt_u8_wc(str).c_str());
	}

	dnd::utf_string String::cvt_u8_utf(const string& str)
	{
		static std::wstring_convert< std::codecvt<utf_char, char, std::mbstate_t>, char32_t> cvt;
		return cvt.from_bytes(str);
	}

	string String::cvt_wc_u8(const wchar_t* str)
	{
		DWORD len = WideCharToMultiByte(CP_UTF8, NULL, str, -1, 0, 0, NULL, NULL) - 1;

		string ret;
		ret.resize(len);

		WideCharToMultiByte(CP_UTF8, NULL, str, -1, ret.data(), len, NULL, NULL);

		return ret;
	}

	string String::cvt_wc_mb(const wchar_t* str, CodePage code_page /*= CP_ACP*/)
	{
		DWORD len = WideCharToMultiByte((UINT)code_page, NULL, str, -1, 0, 0, NULL, NULL);

		string ret;
		ret.resize(len);

		WideCharToMultiByte((UINT)code_page, NULL, str, -1, ret.data(), len, NULL, NULL);

		return ret;
	}

	string String::cvt_mb_u8(const char* str, CodePage code_page /*= CP_ACP*/)
	{
		//TODO:效率优化
		return cvt_wc_u8(cvt_mb_wc(str, code_page).c_str());
	}

	std::wstring String::cvt_mb_wc(const char* str, CodePage code_page /*= CP_ACP*/)
	{
		DWORD len = MultiByteToWideChar((UINT)code_page, NULL, str, -1, 0, 0);

		std::wstring ret;
		ret.resize(len);

		MultiByteToWideChar((UINT)code_page, NULL, str, -1, ret.data(), len);

		return ret;
	}

	string String::cvt_utf_u8(const utf_string& str)
	{
		static std::wstring_convert< std::codecvt<utf_char, char, std::mbstate_t>, char32_t> cvt;
		return cvt.to_bytes(str);
	}

	void String::cpy_u8(char* target, size_t size, const string& source, bool log /*= true*/)
	{
		if (size == 0)
		{
			debug(DL::WARNING, "缓冲区大小为0!");
			return;
		}

		if (source.size() >= size)
		{
			string str_mb = String::cvt_u8_mb(source);

			if (log)
				debug(DL::WARNING, "字符串过比缓冲区大:" + source);

			size_t sub_size = sub_u8(source, size - 1);

			String::Memcpy_s(target, size - 1, source.data(), sub_size);
			
			target[sub_size] = 0;
		}
		else
			strcpy_s(target, size, source.c_str());
	}

	size_t String::sub_u8(const string& str, size_t max)
	{
		if (max == 0)
			return 0;

		//源下标(与子串下标相同)
		size_t i = 0;

		size_t size = str.length();
		size_t cut_len = 0;

		unsigned char ch;
		while (i < size)
		{
			ch = (unsigned char)str[i];
			if (ch >= 252)
				cut_len = 6;
			else if (ch >= 248)
				cut_len = 5;
			else if (ch >= 240)
				cut_len = 4;
			else if (ch >= 224)
				cut_len = 3;
			else if (ch >= 192)
				cut_len = 2;
			else if (ch >= 65 && ch <= 90)
				cut_len = 1;
			else
				cut_len = 1;

			if (i + cut_len > max)
				return i;
			i += cut_len;
		}

		return size;
	}

	string String::Sprintf(const char* format, ...)
	{
		//#include <cstdarg>
		//#include <cstdio>
		va_list args;
		int len;

		va_start(args, format);
		len = vsnprintf(nullptr, 0, format, args) + 1;
		va_end(args);

		string ret;
		ret.resize(len);

		va_start(args, format);
		vsnprintf((char*)ret.data(), len, format, args);
		va_end(args);

		ret.pop_back();

		return ret;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值