千家信息网

UTF-8、UTF-16、UTF-32编码相互转换的方法

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇文章主要介绍"UTF-8、UTF-16、UTF-32编码相互转换的方法",在日常操作中,相信很多人在UTF-8、UTF-16、UTF-32编码相互转换的方法问题上存在疑惑,小编查阅了各式资料,整理
千家信息网最后更新 2025年02月01日UTF-8、UTF-16、UTF-32编码相互转换的方法

这篇文章主要介绍"UTF-8、UTF-16、UTF-32编码相互转换的方法",在日常操作中,相信很多人在UTF-8、UTF-16、UTF-32编码相互转换的方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"UTF-8、UTF-16、UTF-32编码相互转换的方法"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题。

vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8。
gcc则是UTF-8,有无BOM均可(源代码的字符集可以由参数-finput-charset指定)。
那么源代码可以采用带BOM的UTF-8来保存。而windows下的unicode是UTF-16编码;Linux则使用UTF-8或UTF-32。因此不论在哪种系统里,程序在处理字符串时都需要考虑UTF编码之间的相互转换。

下面直接贴出算法代码。算法上我借鉴了秦建辉(http://blog.csdn.net/jhqin)的UnicodeConverter,只是在外面增加了一些泛型处理,让使用相对简单。

核心算法(来自UnicodeConverter):

namespace transform  {      /*         UTF-32 to UTF-8     */         inline static size_t utf(uint32 src, uint8* des)      {          if (src == 0) return 0;             static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };          static const uint32 CODE_UP[] =          {              0x80,           // U+00000000 - U+0000007F              0x800,          // U+00000080 - U+000007FF              0x10000,        // U+00000800 - U+0000FFFF              0x200000,       // U+00010000 - U+001FFFFF              0x4000000,      // U+00200000 - U+03FFFFFF              0x80000000      // U+04000000 - U+7FFFFFFF          };             size_t i, len = sizeof(CODE_UP) / sizeof(uint32);          for(i = 0; i < len; ++i)              if (src < CODE_UP[i]) break;             if (i == len) return 0; // the src is invalid             len = i + 1;          if (des)          {              for(; i > 0; --i)              {                  des[i] = static_cast((src & 0x3F) | 0x80);                  src >>= 6;              }              des[0] = static_cast(src | PREFIX[len - 1]);          }          return len;      }         /*         UTF-8 to UTF-32     */         inline static size_t utf(const uint8* src, uint32& des)      {          if (!src || (*src) == 0) return 0;             uint8 b = *(src++);             if (b < 0x80)          {              des = b;              return 1;          }             if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid             size_t len;             if (b < 0xE0)          {              des = b & 0x1F;              len = 2;          }          else          if (b < 0xF0)          {              des = b & 0x0F;              len = 3;          }          else          if (b < 0xF8)          {              des = b & 0x07;              len = 4;          }          else          if (b < 0xFC)          {              des = b & 0x03;              len = 5;          }          else          {              des = b & 0x01;              len = 6;          }             size_t i = 1;          for (; i < len; ++i)          {              b = *(src++);              if (b < 0x80 || b > 0xBF) return 0; // the src is invalid              des = (des << 6) + (b & 0x3F);          }          return len;      }         /*         UTF-32 to UTF-16     */         inline static size_t utf(uint32 src, uint16* des)      {          if (src == 0) return 0;             if (src <= 0xFFFF)          {              if (des) (*des) = static_cast(src);              return 1;          }          else          if (src <= 0xEFFFF)          {              if (des)              {                  des[0] = static_cast(0xD800 + (src >> 10) - 0x40);  // high                  des[1] = static_cast(0xDC00 + (src & 0x03FF));      // low              }              return 2;          }          return 0;      }         /*         UTF-16 to UTF-32     */         inline static size_t utf(const uint16* src, uint32& des)      {          if (!src || (*src) == 0) return 0;             uint16 w1 = src[0];          if (w1 >= 0xD800 && w1 <= 0xDFFF)          {              if (w1 < 0xDC00)              {                  uint16 w2 = src[1];                  if (w2 >= 0xDC00 && w2 <= 0xDFFF)                  {                      des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);                      return 2;                  }              }              return 0; // the src is invalid          }          else          {              des = w1;              return 1;          }      }  }

上面这些算法都是针对单个字符的,并且是UTF-32和UTF-16/8之间的互转。
通过上面的算法,可以得到UTF-16和UTF-8之间的单字符转换算法:

namespace transform  {      /*         UTF-16 to UTF-8     */         inline static size_t utf(uint16 src, uint8* des)      {          // make utf-16 to utf-32          uint32 tmp;          if (utf(&src, tmp) != 1) return 0;          // make utf-32 to utf-8          return utf(tmp, des);      }         /*         UTF-8 to UTF-16     */         inline static size_t utf(const uint8* src, uint16& des)      {          // make utf-8 to utf-32          uint32 tmp;          size_t len = utf(src, tmp);          if (len == 0) return 0;          // make utf-32 to utf-16          if (utf(tmp, &des) != 1) return 0;          return len;      }  }

同样,通过上面的单字符转换算法,可以得到整个字符串的转换算法:

namespace transform  {      /*         UTF-X: string to string     */         template       size_t utf(const uint32* src, T* des)   // UTF-32 to UTF-X(8/16)      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          for(; *src; ++src)          {              size_t len = utf(*src, des);              if (len == 0) break;              if (des) des += len;              num += len;          }          if (des) (*des) = 0;          return num;      }         template       size_t utf(const T* src, uint32* des)   // UTF-X(8/16) to UTF-32      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          while(*src)          {              uint32 tmp;              size_t len = utf(src, tmp);              if (len == 0) break;              if (des)              {                  (*des) = tmp;                  ++des;              }              src += len;              num += 1;          }          if (des) (*des) = 0;          return num;      }         template       size_t utf(const T* src, U* des)    // UTF-X(8/16) to UTF-Y(16/8)      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          while(*src)          {              // make utf-x to ucs4              uint32 tmp;              size_t len = utf(src, tmp);              if (len == 0) break;              src += len;              // make ucs4 to utf-y              len = utf(tmp, des);              if (len == 0) break;              if (des) des += len;              num += len;          }          if (des) (*des) = 0;          return num;      }  }

有了这些之后,我们已经可以完整的做UTF-8/16/32之间的相互转换了,但是这些函数的使用仍然不是很方便。
比如我现在想把一个UTF-8字符串转换成一个wchar_t*字符串,我得这样写:

const uint8* c = (uint8*)"こんにちわ、世界";  size_t n = (sizeof(wchar_t) == 2) ?      transform::utf(c, (uint16*)0) :      transform::utf(c, (uint32*)0);  wchar_t* s = new wchar_t[n];  if (sizeof(wchar_t) == 2)      transform::utf(c, (uint16*)s);  else      transform::utf(c, (uint32*)s);

这显然是一件很抽搐的事情,因为wchar_t在不同的操作系统(windows/linux)里有不同的sizeof长度。
上面的类型强制转换只是为了去适配合适的函数重载,当然我们也可以通过函数名来区分这些函数:比如分别叫utf8_to_utf32之类的。但是这改变不了写if-else来适配长度的问题。

显然这里可以通过泛型来让算法更好用。
首先,需要被抽离出来的就是参数的类型大小和类型本身的依赖关系:

template  struct utf_type;  template <>         struct utf_type<1> { typedef uint8  type_t; };  template <>         struct utf_type<2> { typedef uint16 type_t; };  template <>         struct utf_type<4> { typedef uint32 type_t; };

然后,实现一个简单的check算法,这样后面就可以利用SFINAE的技巧筛选出合适的算法函数:

template   struct check  {      static const bool value =          ((sizeof(T) == sizeof(typename utf_type::type_t)) && !is_pointer::value);  };

下面我们需要一个detail,即泛型适配的细节。从上面的算法函数参数中,我们可以很容易的观察出一些规律:
只要是由大向小转换(比如32->16,或16->8)的,其对外接口可以抽象成这两种形式:

type_t utf(T src, U* des)  type_t utf(const T* src, U* des)

而由小向大的转换,则是下面这两种形式:

type_t utf(const T* src, U& des)  type_t utf(const T* src, U* des)

再加上第二个指针参数是可以给一个默认值(空指针)的,因此适配的泛型类就可以写成这样:

template  Y), bool = (X != Y)>  struct detail;     /*     UTF-X(32/16) to UTF-Y(16/8) */     template   struct detail  {      typedef typename utf_type::type_t src_t;      typedef typename utf_type::type_t des_t;         template       static typename enable_if::value && check::value,      size_t>::type_t utf(T src, U* des)      {          return transform::utf((src_t)(src), (des_t*)(des));      }         template       static typename enable_if::value,      size_t>::type_t utf(T src)      {          return transform::utf((src_t)(src), (des_t*)(0));      }         template       static typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U* des)      {          return transform::utf((const src_t*)(src), (des_t*)(des));      }         template       static typename enable_if::value,      size_t>::type_t utf(const T* src)      {          return transform::utf((src_t)(src), (des_t*)(0));      }  };     /*     UTF-X(16/8) to UTF-Y(32/16) */     template   struct detail  {      typedef typename utf_type::type_t src_t;      typedef typename utf_type::type_t des_t;         template       static typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U& des)      {          des_t tmp; // for disable the warning strict-aliasing from gcc 4.4          size_t ret = transform::utf((const src_t*)(src), tmp);          des = tmp;          return ret;      }         template       static typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U* des)      {          return transform::utf((const src_t*)(src), (des_t*)(des));      }         template       static typename enable_if::value,      size_t>::type_t utf(const T* src)      {          return transform::utf((const src_t*)(src), (des_t*)(0));      }  };

最后的外敷类收尾就可以相当的简单:

template   struct converter      : detail  {};

通过上面的detail,我们也可以很轻松的写出一个通过指定8、16这些数字,来控制选择哪些转换算法的外敷模板。
有了converter,同类型的需求(指UTF-8转wchar_t)就可以变得轻松愉快很多:

const char* c = "こんにちわ、世界";  wstring s;  size_t n; wchar_t w;  while (!!(n = converter::utf(c, w))) // 这里的!!是为了屏蔽gcc的警告  {      s.push_back(w);      c += n;  }  FILE* fp = fopen("test_converter.txt", "wb");  fwrite(s.c_str(), sizeof(wchar_t), s.length(), fp);  fclose(fp);

上面这一小段代码是将一段UTF-8的文字逐字符转换为wchar_t,并一个个push_back到wstring里,最后把转换完毕的字符串输出到test_converter.txt里。


其实上面的泛型还是显得累赘了。为什么不直接在transform::utf上使用泛型参数呢?
一开始只想到上面那个方法,自然是由于惯性的想要手动指定如何转换编码的缘故,比如最开始的想法,是想做成类似这样的模板:utf<8, 32>(s1, s2),指定两个数字,来决定输入和输出的格式。

后来发现,直接指定字符串/字符的类型或许更加直接些。
现在回头再看看,其实转换所需要的字长(8、16、32)已经在参数的类型中指定了:8bits的char或byte类型肯定不会是用来存放UTF-32的嘛。。
所以只需要把上面核心算法的参数泛型化就可以了。这时代码就会写成下面这个样子:

namespace transform  {      namespace private_      {          template  struct utf_type;          template <>         struct utf_type<1> { typedef uint8  type_t; };          template <>         struct utf_type<2> { typedef uint16 type_t; };          template <>         struct utf_type<4> { typedef uint32 type_t; };             template           struct check          {              static const bool value =                  ((sizeof(T) == sizeof(typename utf_type::type_t)) && !is_pointer::value);          }      }         using namespace transform::private_;         /*         UTF-32 to UTF-8     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(T src, U* des)      {          if (src == 0) return 0;             static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };          static const uint32 CODE_UP[] =          {              0x80,           // U+00000000 - U+0000007F              0x800,          // U+00000080 - U+000007FF              0x10000,        // U+00000800 - U+0000FFFF              0x200000,       // U+00010000 - U+001FFFFF              0x4000000,      // U+00200000 - U+03FFFFFF              0x80000000      // U+04000000 - U+7FFFFFFF          };             size_t i, len = sizeof(CODE_UP) / sizeof(uint32);          for(i = 0; i < len; ++i)              if (src < CODE_UP[i]) break;             if (i == len) return 0; // the src is invalid             len = i + 1;          if (des)          {              for(; i > 0; --i)              {                  des[i] = static_cast((src & 0x3F) | 0x80);                  src >>= 6;              }              des[0] = static_cast(src | PREFIX[len - 1]);          }          return len;      }         /*         UTF-8 to UTF-32     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U& des)      {          if (!src || (*src) == 0) return 0;             uint8 b = *(src++);             if (b < 0x80)          {              des = b;              return 1;          }             if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid             size_t len;             if (b < 0xE0)          {              des = b & 0x1F;              len = 2;          }          else          if (b < 0xF0)          {              des = b & 0x0F;              len = 3;          }          else          if (b < 0xF8)          {              des = b & 0x07;              len = 4;          }          else          if (b < 0xFC)          {              des = b & 0x03;              len = 5;          }          else          {              des = b & 0x01;              len = 6;          }             size_t i = 1;          for (; i < len; ++i)          {              b = *(src++);              if (b < 0x80 || b > 0xBF) return 0; // the src is invalid              des = (des << 6) + (b & 0x3F);          }          return len;      }         /*         UTF-32 to UTF-16     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(T src, U* des)      {          if (src == 0) return 0;             if (src <= 0xFFFF)          {              if (des) (*des) = static_cast(src);              return 1;          }          else          if (src <= 0xEFFFF)          {              if (des)              {                  des[0] = static_cast(0xD800 + (src >> 10) - 0x40);  // high                  des[1] = static_cast(0xDC00 + (src & 0x03FF));      // low              }              return 2;          }          return 0;      }         /*         UTF-16 to UTF-32     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U& des)      {          if (!src || (*src) == 0) return 0;             uint16 w1 = src[0];          if (w1 >= 0xD800 && w1 <= 0xDFFF)          {              if (w1 < 0xDC00)              {                  uint16 w2 = src[1];                  if (w2 >= 0xDC00 && w2 <= 0xDFFF)                  {                      des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);                      return 2;                  }              }              return 0; // the src is invalid          }          else          {              des = w1;              return 1;          }      }         /*         UTF-16 to UTF-8     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(T src, U* des)      {          // make utf-16 to utf-32          uint32 tmp;          if (utf(&src, tmp) != 1) return 0;          // make utf-32 to utf-8          return utf(tmp, des);      }         /*         UTF-8 to UTF-16     */         template       typename enable_if::value && check::value,      size_t>::type_t utf(const T* src, U& des)      {          // make utf-8 to utf-32          uint32 tmp;          size_t len = utf(src, tmp);          if (len == 0) return 0;          // make utf-32 to utf-16          if (utf(tmp, &des) != 1) return 0;          return len;      }         /*         UTF-X: string to string     */         template       typename enable_if::value && (check::value || check::value),      size_t>::type_t utf(const T* src, U* des)   // UTF-32 to UTF-X(8/16)      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          for(; *src; ++src)          {              size_t len = utf(*src, des);              if (len == 0) break;              if (des) des += len;              num += len;          }          if (des) (*des) = 0;          return num;      }         template       typename enable_if<(check::value || check::value) && check::value,      size_t>::type_t utf(const T* src, U* des)   // UTF-X(8/16) to UTF-32      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          while(*src)          {              uint32 tmp;              size_t len = utf(src, tmp);              if (len == 0) break;              if (des)              {                  (*des) = tmp;                  ++des;              }              src += len;              num += 1;          }          if (des) (*des) = 0;          return num;      }         template       typename enable_if<(check::value && check::value) ||                         (check::value && check::value),      size_t>::type_t utf(const T* src, U* des)    // UTF-X(8/16) to UTF-Y(16/8)      {          if (!src || (*src) == 0) return 0;             size_t num = 0;          while(*src)          {              // make utf-x to utf-32              uint32 tmp;              size_t len = utf(src, tmp);              if (len == 0) break;              src += len;              // make utf-32 to utf-y              len = utf(tmp, des);              if (len == 0) break;              if (des) des += len;              num += len;          }          if (des) (*des) = 0;          return num;      }  }

这样用起来就更加简单了:

const char* c = "你好世界";  size_t n = nx::transform::utf(c, (wchar_t*)0);

完整代码请参考:
https://code.google.com/p/nixy/source/browse/trunk/nixycore/string/transform.h

到此,关于"UTF-8、UTF-16、UTF-32编码相互转换的方法"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

UTF-8 算法 字符 编码 参数 字符串 类型 方法 函数 面的 代码 之间 utf-8 学习 适配 世界 问题 不同 合适 单字 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 app软件开发要多少费用 卫宁互联网科技有限公司 昆仑通泰实时数据库添加变量 双随机一公开网络安全 特殊时期网络安全防范工作 锐捷网络技术支持工程师校招面试 运输能耗数据库 能源行业的网络安全培训内容 数据库抽取大数据中心数据 猫王互联网科技公司是中介吗 关于网络安全的知识内容防范 校园网络安全情景短剧剧本 税控服务器管理在哪里 网络技术属于计算机的应用领域 江苏微型软件开发产业化 辽宁移动dns服务器地址云主机 软件开发跟踪 tracker服务器搭建 安仁计算机软件开发 上海优质软件开发报价 卫宁互联网科技有限公司 网络安全发 验证手机 网络技术的基本概念是什么 好的管理软件开发平台 停服务器视频教学 数据库自增id可以修改初始值吗 手机软件开发需要哪些 网络安全知识手抄报主题 access数据库答题系统 web服务器程序下载
0