简介

1、这段代码只考虑在小端序情况下的转换(一般的机器都是的)。

2、这段代码需要C++11的支持(只是用到了u16string),如果不支持,可以添加下面代码

typedef uint16_t char16_t;
typedef std::basic_string<char16_t>

utfconvert.h

#ifndef __UTFCONVERT_H__
#define __UTFCONVERT_H__
#include <string> // 从UTF16编码字符串构建,需要带BOM标记
std::string utf16_to_utf8(const std::u16string& u16str); // 从UTF16 LE编码的字符串创建
std::string utf16le_to_utf8(const std::u16string& u16str); // 从UTF16BE编码字符串创建
std::string utf16be_to_utf8(const std::u16string& u16str); // 获取转换为UTF-16 LE编码的字符串
std::u16string utf8_to_utf16le(const std::string& u8str, bool addbom = false, bool* ok = NULL); // 获取转换为UTF-16 BE的字符串
std::u16string utf8_to_utf16be(const std::string& u8str, bool addbom = false, bool* ok = NULL); #endif //! __UTFCONVERT_H__

utfconvert.cpp

#include "utfconvert.h"

#include <stdint.h>
#ifdef __GNUC__
#include <endian.h>
#endif // __GNUC__ static inline uint16_t byteswap_ushort(uint16_t number)
{
#if defined(_MSC_VER) && _MSC_VER > 1310
return _byteswap_ushort(number);
#elif defined(__GNUC__)
return __builtin_bswap16(number);
#else
return (number >> 8) | (number << 8);
#endif
} ////////////////////////////////////////
// 以下转换都是在小端序下进行 //
//////////////////////////////////////// // 从UTF16编码字符串构建,需要带BOM标记
std::string utf16_to_utf8(const std::u16string& u16str)
{
if (u16str.empty()){ return std::string(); }
//Byte Order Mark
char16_t bom = u16str[0];
switch (bom){
case 0xFEFF: //Little Endian
return utf16le_to_utf8(u16str);
break;
case 0xFFFE: //Big Endian
return utf16be_to_utf8(u16str);
break;
default:
return std::string();
}
} // 从UTF16 LE编码的字符串创建
std::string utf16le_to_utf8(const std::u16string& u16str)
{
if (u16str.empty()){ return std::string(); }
const char16_t* p = u16str.data();
std::u16string::size_type len = u16str.length();
if (p[0] == 0xFEFF){
p += 1; //带有bom标记,后移
len -= 1;
} // 开始转换
std::string u8str;
u8str.reserve(len * 3); char16_t u16char;
for (std::u16string::size_type i = 0; i < len; ++i){
// 这里假设是在小端序下(大端序不适用)
u16char = p[i]; // 1字节表示部分
if (u16char < 0x0080){
// u16char <= 0x007f
// U- 0000 0000 ~ 0000 07ff : 0xxx xxxx
u8str.push_back((char)(u16char & 0x00FF)); // 取低8bit
continue;
}
// 2 字节能表示部分
if (u16char >= 0x0080 && u16char <= 0x07FF){
// * U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
u8str.push_back((char)(((u16char >> 6) & 0x1F) | 0xC0));
u8str.push_back((char)((u16char & 0x3F) | 0x80));
continue;
}
// 代理项对部分(4字节表示)
if (u16char >= 0xD800 && u16char <= 0xDBFF) {
// * U-00010000 - U-001FFFFF: 1111 0xxx 10xxxxxx 10xxxxxx 10xxxxxx
uint32_t highSur = u16char;
uint32_t lowSur = p[++i];
// 从代理项对到UNICODE代码点转换
// 1、从高代理项减去0xD800,获取有效10bit
// 2、从低代理项减去0xDC00,获取有效10bit
// 3、加上0x10000,获取UNICODE代码点值
uint32_t codePoint = highSur - 0xD800;
codePoint <<= 10;
codePoint |= lowSur - 0xDC00;
codePoint += 0x10000;
// 转为4字节UTF8编码表示
u8str.push_back((char)((codePoint >> 18) | 0xF0));
u8str.push_back((char)(((codePoint >> 12) & 0x3F) | 0x80));
u8str.push_back((char)(((codePoint >> 06) & 0x3F) | 0x80));
u8str.push_back((char)((codePoint & 0x3F) | 0x80));
continue;
}
// 3 字节表示部分
{
// * U-0000E000 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
u8str.push_back((char)(((u16char >> 12) & 0x0F) | 0xE0));
u8str.push_back((char)(((u16char >> 6) & 0x3F) | 0x80));
u8str.push_back((char)((u16char & 0x3F) | 0x80));
continue;
}
} return u8str;
} // 从UTF16BE编码字符串创建
std::string utf16be_to_utf8(const std::u16string& u16str)
{
if (u16str.empty()){ return std::string(); }
const char16_t* p = u16str.data();
std::u16string::size_type len = u16str.length();
if (p[0] == 0xFEFF){
p += 1; //带有bom标记,后移
len -= 1;
} // 开始转换
std::string u8str;
u8str.reserve(len * 2);
char16_t u16char; //u16le 低字节存低位,高字节存高位
for (std::u16string::size_type i = 0; i < len; ++i) {
// 这里假设是在小端序下(大端序不适用)
u16char = p[i];
// 将大端序转为小端序
u16char = byteswap_ushort(u16char); // 1字节表示部分
if (u16char < 0x0080) {
// u16char <= 0x007f
// U- 0000 0000 ~ 0000 07ff : 0xxx xxxx
u8str.push_back((char)(u16char & 0x00FF));
continue;
}
// 2 字节能表示部分
if (u16char >= 0x0080 && u16char <= 0x07FF) {
// * U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
u8str.push_back((char)(((u16char >> 6) & 0x1F) | 0xC0));
u8str.push_back((char)((u16char & 0x3F) | 0x80));
continue;
}
// 代理项对部分(4字节表示)
if (u16char >= 0xD800 && u16char <= 0xDBFF) {
// * U-00010000 - U-001FFFFF: 1111 0xxx 10xxxxxx 10xxxxxx 10xxxxxx
uint32_t highSur = u16char;
uint32_t lowSur = byteswap_ushort(p[++i]);
// 从代理项对到UNICODE代码点转换
// 1、从高代理项减去0xD800,获取有效10bit
// 2、从低代理项减去0xDC00,获取有效10bit
// 3、加上0x10000,获取UNICODE代码点值
uint32_t codePoint = highSur - 0xD800;
codePoint <<= 10;
codePoint |= lowSur - 0xDC00;
codePoint += 0x10000;
// 转为4字节UTF8编码表示
u8str.push_back((char)((codePoint >> 18) | 0xF0));
u8str.push_back((char)(((codePoint >> 12) & 0x3F) | 0x80));
u8str.push_back((char)(((codePoint >> 06) & 0x3F) | 0x80));
u8str.push_back((char)((codePoint & 0x3F) | 0x80));
continue;
}
// 3 字节表示部分
{
// * U-0000E000 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
u8str.push_back((char)(((u16char >> 12) & 0x0F) | 0xE0));
u8str.push_back((char)(((u16char >> 6) & 0x3F) | 0x80));
u8str.push_back((char)((u16char & 0x3F) | 0x80));
continue;
}
}
return u8str;
} // 获取转换为UTF-16 LE编码的字符串
std::u16string utf8_to_utf16le(const std::string& u8str, bool addbom, bool* ok)
{
std::u16string u16str;
u16str.reserve(u8str.size());
if (addbom) {
u16str.push_back(0xFEFF); //bom (字节表示为 FF FE)
}
std::string::size_type len = u8str.length(); const unsigned char* p = (unsigned char*)(u8str.data());
// 判断是否具有BOM(判断长度小于3字节的情况)
if (len > 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF){
p += 3;
len -= 3;
} bool is_ok = true;
// 开始转换
for (std::string::size_type i = 0; i < len; ++i) {
uint32_t ch = p[i]; // 取出UTF8序列首字节
if ((ch & 0x80) == 0) {
// 最高位为0,只有1字节表示UNICODE代码点
u16str.push_back((char16_t)ch);
continue;
}
switch (ch & 0xF0)
{
case 0xF0: // 4 字节字符, 0x10000 到 0x10FFFF
{
uint32_t c2 = p[++i];
uint32_t c3 = p[++i];
uint32_t c4 = p[++i];
// 计算UNICODE代码点值(第一个字节取低3bit,其余取6bit)
uint32_t codePoint = ((ch & 0x07U) << 18) | ((c2 & 0x3FU) << 12) | ((c3 & 0x3FU) << 6) | (c4 & 0x3FU);
if (codePoint >= 0x10000)
{
// 在UTF-16中 U+10000 到 U+10FFFF 用两个16bit单元表示, 代理项对.
// 1、将代码点减去0x10000(得到长度为20bit的值)
// 2、high 代理项 是将那20bit中的高10bit加上0xD800(110110 00 00000000)
// 3、low 代理项 是将那20bit中的低10bit加上0xDC00(110111 00 00000000)
codePoint -= 0x10000;
u16str.push_back((char16_t)((codePoint >> 10) | 0xD800U));
u16str.push_back((char16_t)((codePoint & 0x03FFU) | 0xDC00U));
}
else
{
// 在UTF-16中 U+0000 到 U+D7FF 以及 U+E000 到 U+FFFF 与Unicode代码点值相同.
// U+D800 到 U+DFFF 是无效字符, 为了简单起见,这里假设它不存在(如果有则不编码)
u16str.push_back((char16_t)codePoint);
}
}
break;
case 0xE0: // 3 字节字符, 0x800 到 0xFFFF
{
uint32_t c2 = p[++i];
uint32_t c3 = p[++i];
// 计算UNICODE代码点值(第一个字节取低4bit,其余取6bit)
uint32_t codePoint = ((ch & 0x0FU) << 12) | ((c2 & 0x3FU) << 6) | (c3 & 0x3FU);
u16str.push_back((char16_t)codePoint);
}
break;
case 0xD0: // 2 字节字符, 0x80 到 0x7FF
case 0xC0:
{
uint32_t c2 = p[++i];
// 计算UNICODE代码点值(第一个字节取低5bit,其余取6bit)
uint32_t codePoint = ((ch & 0x1FU) << 12) | ((c2 & 0x3FU) << 6);
u16str.push_back((char16_t)codePoint);
}
break;
default: // 单字节部分(前面已经处理,所以不应该进来)
is_ok = false;
break;
}
}
if (ok != NULL) { *ok = is_ok; } return u16str;
} // 获取转换为UTF-16 BE的字符串
std::u16string utf8_to_utf16be(const std::string& u8str, bool addbom, bool* ok)
{
// 先获取utf16le编码字符串
std::u16string u16str = utf8_to_utf16le(u8str, addbom, ok);
// 将小端序转换为大端序
for (size_t i = 0; i < u16str.size(); ++i) {
u16str[i] = byteswap_ushort(u16str[i]);
}
return u16str;
}

C++ UTF8和UTF16互转代码的更多相关文章

  1. JS中UTF-8和UTF-16互转

    1.由于服务端使用的Go,默认是使用UTF-8编码的,而JS默认是Unicode编码的(也就是UTF-16),所以为了字符串编码的一致性,将前端字符串数据编码转换为UTF-8之后再发送给服务端,服务端 ...

  2. UTF8 与 UTF16 编码

    Unicode 的发展,英文好的直接去 unicode.org 上去看吧,不好的可以移步到这里 看dengyunze的总结:<关于UTF8,UTF16,UTF32,UTF16-LE,UTF16- ...

  3. UTF-8、UTF-16、UTF-32编码的相互转换

    最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题. vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8.gcc ...

  4. UTF-8、UTF-16、UTF-32编码的相互转换(不使用现成的函数)

    最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题. vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8.gcc ...

  5. 简单几句话总结Unicode,UTF-8和UTF-16

    概念 先说一说基本的概念,这包括什么是Unicode,什么是UTF-8,什么是UTF-16. Unicode,UTF-8,UTF-16完整的说明请参考Wiki(Unicode,UTF-8,UTF-16 ...

  6. Unicode、UTF-8、UTF-16和UTF-32的区别

    Unicode是一个巨大的字符集,给世界上所有的字符定义了一个唯一编码.其仅仅规定了每个符号的二进制代码,没有制定细化的存储规则.UTF-8.UTF-16.UTF-32才是Unicode的存储格式定义 ...

  7. 字符编码的种类:ASCII、GB2312、GBK、GB18030、Unicode、UTF-8、UTF-16、Base64

    ASCII码ASCII:https://zh.wikipedia.org/wiki/ASCIIASCII(American Standard Code for Information Intercha ...

  8. 聊聊计算机中的编码(Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等)以及乱码问题的解决办法

    作为一个程序员,一个中国的程序员,想来“乱码”问题基本上都遇到过,也为之头疼过.出现乱码问题的根本原因是编码与解码使用了不同而且不兼容的“标准”,在国内一般出现在中文的编解码过程中. 我们平时常见的编 ...

  9. 字符编码的故事:ASCII,GB2312,Unicode,UTF-8,UTF-16

    http://blog.csdn.net/longintchar/article/details/51079340 ****************************************** ...

随机推荐

  1. ImportError: No module named model_libs

    在运行ssd时遇到这个问题 实际是python接口的路径不对,使用echo $$PYTHONPATH  弹出当前python路径,发现是caffe自己的python接口,采用 export PYTHO ...

  2. MSSQL2008 全文索引的创建

    从MSSQL2008开始,全文索引推荐的创建方式已经与2005不同了.对于字符类型的数据库,可以直接创建. CREATE UNIQUE INDEX hr_job_idx ON hr_job_datab ...

  3. CSS 强制换行和禁止换行强制换行 和禁止换行样式

    强制换行 1.word-break: break-all;       只对英文起作用,以字母作为换行依据. 2.word-wrap: break-word;   只对英文起作用,以单词作为换行依据. ...

  4. 用SLF4j/Logback打印日志-1

    在 浅谈后端日志系统 中已经写了很多日志方面的零散的非技术的东西.本篇更像一份入门说明,讲解一下SLF4j/Logback.SLF4J是一套抽象的日志API接口,logback它是的底层实现,所以在这 ...

  5. Domino(群组工作软件)

    ylbtech-Miscellaneos: Domino(群组工作软件) “Domino”是一种群组工作软件.使用了Domino的服务器就叫Domino服务器Domino的功能强大,界面丰富,主要用于 ...

  6. iOS:麦克风权限检测和获取

    一.检测 该方法是用来判断麦克风是否进行过授权,如果授权过就直接进行需要的功能操作:如果没有进行授权,那么就要获取授权. AVAuthorizationStatus authStatus = [AVC ...

  7. cesium原理篇(二)--网格划分【转】

    转自:http://www.cnblogs.com/fuckgiser/p/5772077.html 上一篇我们从宏观上介绍了Cesium的渲染过程,本章延续上一章的内容,详细介绍一下Cesium网格 ...

  8. MFC中位图的显示

    分析: 首先,我们要明确一点,窗口的绘制包括两个步骤,首先:擦除窗口背景,然后再对窗口重新进行绘制:当擦除窗口背景时,程序会发生一个WM_ERASEBKGND消息,因此可以在此响应函数中完成位图的显示 ...

  9. ORACLE中union/union all/Intersect/Minus用法

    Union,对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序: Union All,对两个结果集进行并集操作,包括重复行,不进行排序: Intersect,对两个结果集进行交集操作,不包 ...

  10. AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法

    AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法 2018-01-18 16:13蚂蚁金服/雾霾/人工智能 导读:词向量算法是自然语言处理领域的基础算法,在序列标注.问答系统和机 ...