由于我准备换用iconv库来进行字符编码转换,所以记录一下windows平台MultiByteToWideChar和WideCharToMultiByte的用法,以便以后万一用到。
//.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;
}
}