【转】UTF-8、UTF-16、UTF-32编码的相互转换

时间:2021-10-29 来源:未知网络 作者:996建站网

相关学习资料:

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

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编码之间的相互转换。

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

  1. namespace transform
  2. {
  3. /*
  4. UTF-32 to UTF-8
  5. */
  6. inline static size_t utf(uint32 src, uint8* des)
  7. {
  8. if (src == 0) return 0;
  9. static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  10. static const uint32 CODE_UP[] =
  11. {
  12. 0x80, // U+00000000 – U+0000007F
  13. 0x800, // U+00000080 – U+000007FF
  14. 0x10000, // U+00000800 – U+0000FFFF
  15. 0x200000, // U+00010000 – U+001FFFFF
  16. 0x4000000, // U+00200000 – U+03FFFFFF
  17. 0x80000000 // U+04000000 – U+7FFFFFFF
  18. };
  19. size_t i, len = sizeof(CODE_UP) / sizeof(uint32);
  20. for(i = 0; i < len; ++i)
  21. if (src < CODE_UP[i]) break;
  22. if (i == len) return 0; // the src is invalid
  23. len = i + 1;
  24. if (des)
  25. {
  26. for(; i > 0; –i)
  27. {
  28. des[i] = static_cast<uint8>((src & 0x3F) | 0x80);
  29. src >>= 6;
  30. }
  31. des[0] = static_cast<uint8>(src | PREFIX[len – 1]);
  32. }
  33. return len;
  34. }
  35. /*
  36. UTF-8 to UTF-32
  37. */
  38. inline static size_t utf(const uint8* src, uint32& des)
  39. {
  40. if (!src || (*src) == 0) return 0;
  41. uint8 b = *(src++);
  42. if (b < 0x80)
  43. {
  44. des = b;
  45. return 1;
  46. }
  47. if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid
  48. size_t len;
  49. if (b < 0xE0)
  50. {
  51. des = b & 0x1F;
  52. len = 2;
  53. }
  54. else
  55. if (b < 0xF0)
  56. {
  57. des = b & 0x0F;
  58. len = 3;
  59. }
  60. else
  61. if (b < 0xF8)
  62. {
  63. des = b & 0x07;
  64. len = 4;
  65. }
  66. else
  67. if (b < 0xFC)
  68. {
  69. des = b & 0x03;
  70. len = 5;
  71. }
  72. else
  73. {
  74. des = b & 0x01;
  75. len = 6;
  76. }
  77. size_t i = 1;
  78. for (; i < len; ++i)
  79. {
  80. b = *(src++);
  81. if (b < 0x80 || b > 0xBF) return 0; // the src is invalid
  82. des = (des << 6) + (b & 0x3F);
  83. }
  84. return len;
  85. }
  86. /*
  87. UTF-32 to UTF-16
  88. */
  89. inline static size_t utf(uint32 src, uint16* des)
  90. {
  91. if (src == 0) return 0;
  92. if (src <= 0xFFFF)
  93. {
  94. if (des) (*des) = static_cast<uint16>(src);
  95. return 1;
  96. }
  97. else
  98. if (src <= 0xEFFFF)
  99. {
  100. if (des)
  101. {
  102. des[0] = static_cast<uint16>(0xD800 + (src >> 10) – 0x40); // high
  103. des[1] = static_cast<uint16>(0xDC00 + (src & 0x03FF)); // low
  104. }
  105. return 2;
  106. }
  107. return 0;
  108. }
  109. /*
  110. UTF-16 to UTF-32
  111. */
  112. inline static size_t utf(const uint16* src, uint32& des)
  113. {
  114. if (!src || (*src) == 0) return 0;
  115. uint16 w1 = src[0];
  116. if (w1 >= 0xD800 && w1 <= 0xDFFF)
  117. {
  118. if (w1 < 0xDC00)
  119. {
  120. uint16 w2 = src[1];
  121. if (w2 >= 0xDC00 && w2 <= 0xDFFF)
  122. {
  123. des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);
  124. return 2;
  125. }
  126. }
  127. return 0; // the src is invalid
  128. }
  129. else
  130. {
  131. des = w1;
  132. return 1;
  133. }
  134. }
  135. }

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

namespace transform

  1. {
  2. /*
  3. UTF-16 to UTF-8
  4. */
  5. inline static size_t utf(uint16 src, uint8* des)
  6. {
  7. // make utf-16 to utf-32
  8. uint32 tmp;
  9. if (utf(&src, tmp) != 1) return 0;
  10. // make utf-32 to utf-8
  11. return utf(tmp, des);
  12. }
  13. /*
  14. UTF-8 to UTF-16
  15. */
  16. inline static size_t utf(const uint8* src, uint16& des)
  17. {
  18. // make utf-8 to utf-32
  19. uint32 tmp;
  20. size_t len = utf(src, tmp);
  21. if (len == 0) return 0;
  22. // make utf-32 to utf-16
  23. if (utf(tmp, &des) != 1) return 0;
  24. return len;
  25. }
  26. }

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

namespace transform

  1. {
  2. /*
  3. UTF-X: string to string
  4. */
  5. template <typename T>
  6. size_t utf(const uint32* src, T* des) // UTF-32 to UTF-X(8/16)
  7. {
  8. if (!src || (*src) == 0) return 0;
  9. size_t num = 0;
  10. for(; *src; ++src)
  11. {
  12. size_t len = utf(*src, des);
  13. if (len == 0) break;
  14. if (des) des += len;
  15. num += len;
  16. }
  17. if (des) (*des) = 0;
  18. return num;
  19. }
  20. template <typename T>
  21. size_t utf(const T* src, uint32* des) // UTF-X(8/16) to UTF-32
  22. {
  23. if (!src || (*src) == 0) return 0;
  24. size_t num = 0;
  25. while(*src)
  26. {
  27. uint32 tmp;
  28. size_t len = utf(src, tmp);
  29. if (len == 0) break;
  30. if (des)
  31. {
  32. (*des) = tmp;
  33. ++des;
  34. }
  35. src += len;
  36. num += 1;
  37. }
  38. if (des) (*des) = 0;
  39. return num;
  40. }
  41. template <typename T, typename U>
  42. size_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-Y(16/8)
  43. {
  44. if (!src || (*src) == 0) return 0;
  45. size_t num = 0;
  46. while(*src)
  47. {
  48. // make utf-x to ucs4
  49. uint32 tmp;
  50. size_t len = utf(src, tmp);
  51. if (len == 0) break;
  52. src += len;
  53. // make ucs4 to utf-y
  54. len = utf(tmp, des);
  55. if (len == 0) break;
  56. if (des) des += len;
  57. num += len;
  58. }
  59. if (des) (*des) = 0;
  60. return num;
  61. }
  62. }

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

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

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

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

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

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

  1. template <size_t X, typename T>
  2. struct check
  3. {
  4. static const bool value =
  5. ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);
  6. };

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

[cpp] view plaincopy

  1. type_t utf(T src, U* des)
  2. type_t utf(const T* src, U* des)

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

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

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

  1. template <size_t X, size_t Y, bool = (X > Y), bool = (X != Y)>
  2. struct detail;
  3. /*
  4. UTF-X(32/16) to UTF-Y(16/8)
  5. */
  6. template <size_t X, size_t Y>
  7. struct detail<X, Y, true, true>
  8. {
  9. typedef typename utf_type<X>::type_t src_t;
  10. typedef typename utf_type<Y>::type_t des_t;
  11. template <typename T, typename U>
  12. static typename enable_if<check<X, T>::value && check<Y, U>::value,
  13. size_t>::type_t utf(T src, U* des)
  14. {
  15. return transform::utf((src_t)(src), (des_t*)(des));
  16. }
  17. template <typename T>
  18. static typename enable_if<check<X, T>::value,
  19. size_t>::type_t utf(T src)
  20. {
  21. return transform::utf((src_t)(src), (des_t*)(0));
  22. }
  23. template <typename T, typename U>
  24. static typename enable_if<check<X, T>::value && check<Y, U>::value,
  25. size_t>::type_t utf(const T* src, U* des)
  26. {
  27. return transform::utf((const src_t*)(src), (des_t*)(des));
  28. }
  29. template <typename T>
  30. static typename enable_if<check<X, T>::value,
  31. size_t>::type_t utf(const T* src)
  32. {
  33. return transform::utf((src_t)(src), (des_t*)(0));
  34. }
  35. };
  36. /*
  37. UTF-X(16/8) to UTF-Y(32/16)
  38. */
  39. template <size_t X, size_t Y>
  40. struct detail<X, Y, false, true>
  41. {
  42. typedef typename utf_type<X>::type_t src_t;
  43. typedef typename utf_type<Y>::type_t des_t;
  44. template <typename T, typename U>
  45. static typename enable_if<check<X, T>::value && check<Y, U>::value,
  46. size_t>::type_t utf(const T* src, U& des)
  47. {
  48. des_t tmp; // for disable the warning strict-aliasing from gcc 4.4
  49. size_t ret = transform::utf((const src_t*)(src), tmp);
  50. des = tmp;
  51. return ret;
  52. }
  53. template <typename T, typename U>
  54. static typename enable_if<check<X, T>::value && check<Y, U>::value,
  55. size_t>::type_t utf(const T* src, U* des)
  56. {
  57. return transform::utf((const src_t*)(src), (des_t*)(des));
  58. }
  59. template <typename T>
  60. static typename enable_if<check<X, T>::value,
  61. size_t>::type_t utf(const T* src)
  62. {
  63. return transform::utf((const src_t*)(src), (des_t*)(0));
  64. }
  65. };

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

[cpp] view plaincopy

  1. template <typename T, typename U>
  2. struct converter
  3. : detail<sizeof(T), sizeof(U)>
  4. {};

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

const char* c = "こんにちわ、世界";

  1. wstring s;
  2. size_t n; wchar_t w;
  3. while (!!(n = converter<char, wchar_t>::utf(c, w))) // 这里的!!是为了屏蔽gcc的警告
  4. {
  5. s.push_back(w);
  6. c += n;
  7. }
  8. FILE* fp = fopen("test_converter.txt", "wb");
  9. fwrite(s.c_str(), sizeof(wchar_t), s.length(), fp);
  10. 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的嘛。。
所以只需要把上面核心算法的参数泛型化就可以了。这时代码就会写成下面这个样子:

  1. namespace transform
  2. {
  3. namespace private_
  4. {
  5. template <size_t X> struct utf_type;
  6. template <> struct utf_type<1> { typedef uint8 type_t; };
  7. template <> struct utf_type<2> { typedef uint16 type_t; };
  8. template <> struct utf_type<4> { typedef uint32 type_t; };
  9. template <typename T, size_t X>
  10. struct check
  11. {
  12. static const bool value =
  13. ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);
  14. }
  15. }
  16. using namespace transform::private_;
  17. /*
  18. UTF-32 to UTF-8
  19. */
  20. template <typename T, typename U>
  21. typename enable_if<check<T, 4>::value && check<U, 1>::value,
  22. size_t>::type_t utf(T src, U* des)
  23. {
  24. if (src == 0) return 0;
  25. static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  26. static const uint32 CODE_UP[] =
  27. {
  28. 0x80, // U+00000000 – U+0000007F
  29. 0x800, // U+00000080 – U+000007FF
  30. 0x10000, // U+00000800 – U+0000FFFF
  31. 0x200000, // U+00010000 – U+001FFFFF
  32. 0x4000000, // U+00200000 – U+03FFFFFF
  33. 0x80000000 // U+04000000 – U+7FFFFFFF
  34. };
  35. size_t i, len = sizeof(CODE_UP) / sizeof(uint32);
  36. for(i = 0; i < len; ++i)
  37. if (src < CODE_UP[i]) break;
  38. if (i == len) return 0; // the src is invalid
  39. len = i + 1;
  40. if (des)
  41. {
  42. for(; i > 0; –i)
  43. {
  44. des[i] = static_cast<U>((src & 0x3F) | 0x80);
  45. src >>= 6;
  46. }
  47. des[0] = static_cast<U>(src | PREFIX[len – 1]);
  48. }
  49. return len;
  50. }
  51. /*
  52. UTF-8 to UTF-32
  53. */
  54. template <typename T, typename U>
  55. typename enable_if<check<T, 1>::value && check<U, 4>::value,
  56. size_t>::type_t utf(const T* src, U& des)
  57. {
  58. if (!src || (*src) == 0) return 0;
  59. uint8 b = *(src++);
  60. if (b < 0x80)
  61. {
  62. des = b;
  63. return 1;
  64. }
  65. if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid
  66. size_t len;
  67. if (b < 0xE0)
  68. {
  69. des = b & 0x1F;
  70. len = 2;
  71. }
  72. else
  73. if (b < 0xF0)
  74. {
  75. des = b & 0x0F;
  76. len = 3;
  77. }
  78. else
  79. if (b < 0xF8)
  80. {
  81. des = b & 0x07;
  82. len = 4;
  83. }
  84. else
  85. if (b < 0xFC)
  86. {
  87. des = b & 0x03;
  88. len = 5;
  89. }
  90. else
  91. {
  92. des = b & 0x01;
  93. len = 6;
  94. }
  95. size_t i = 1;
  96. for (; i < len; ++i)
  97. {
  98. b = *(src++);
  99. if (b < 0x80 || b > 0xBF) return 0; // the src is invalid
  100. des = (des << 6) + (b & 0x3F);
  101. }
  102. return len;
  103. }
  104. /*
  105. UTF-32 to UTF-16
  106. */
  107. template <typename T, typename U>
  108. typename enable_if<check<T, 4>::value && check<U, 2>::value,
  109. size_t>::type_t utf(T src, U* des)
  110. {
  111. if (src == 0) return 0;
  112. if (src <= 0xFFFF)
  113. {
  114. if (des) (*des) = static_cast<U>(src);
  115. return 1;
  116. }
  117. else
  118. if (src <= 0xEFFFF)
  119. {
  120. if (des)
  121. {
  122. des[0] = static_cast<U>(0xD800 + (src >> 10) – 0x40); // high
  123. des[1] = static_cast<U>(0xDC00 + (src & 0x03FF)); // low
  124. }
  125. return 2;
  126. }
  127. return 0;
  128. }
  129. /*
  130. UTF-16 to UTF-32
  131. */
  132. template <typename T, typename U>
  133. typename enable_if<check<T, 2>::value && check<U, 4>::value,
  134. size_t>::type_t utf(const T* src, U& des)
  135. {
  136. if (!src || (*src) == 0) return 0;
  137. uint16 w1 = src[0];
  138. if (w1 >= 0xD800 && w1 <= 0xDFFF)
  139. {
  140. if (w1 < 0xDC00)
  141. {
  142. uint16 w2 = src[1];
  143. if (w2 >= 0xDC00 && w2 <= 0xDFFF)
  144. {
  145. des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);
  146. return 2;
  147. }
  148. }
  149. return 0; // the src is invalid
  150. }
  151. else
  152. {
  153. des = w1;
  154. return 1;
  155. }
  156. }
  157. /*
  158. UTF-16 to UTF-8
  159. */
  160. template <typename T, typename U>
  161. typename enable_if<check<T, 2>::value && check<U, 1>::value,
  162. size_t>::type_t utf(T src, U* des)
  163. {
  164. // make utf-16 to utf-32
  165. uint32 tmp;
  166. if (utf(&src, tmp) != 1) return 0;
  167. // make utf-32 to utf-8
  168. return utf(tmp, des);
  169. }
  170. /*
  171. UTF-8 to UTF-16
  172. */
  173. template <typename T, typename U>
  174. typename enable_if<check<T, 1>::value && check<U, 2>::value,
  175. size_t>::type_t utf(const T* src, U& des)
  176. {
  177. // make utf-8 to utf-32
  178. uint32 tmp;
  179. size_t len = utf(src, tmp);
  180. if (len == 0) return 0;
  181. // make utf-32 to utf-16
  182. if (utf(tmp, &des) != 1) return 0;
  183. return len;
  184. }
  185. /*
  186. UTF-X: string to string
  187. */
  188. template <typename T, typename U>
  189. typename enable_if<check<T, 4>::value && (check<U, 1>::value || check<U, 2>::value),
  190. size_t>::type_t utf(const T* src, U* des) // UTF-32 to UTF-X(8/16)
  191. {
  192. if (!src || (*src) == 0) return 0;
  193. size_t num = 0;
  194. for(; *src; ++src)
  195. {
  196. size_t len = utf(*src, des);
  197. if (len == 0) break;
  198. if (des) des += len;
  199. num += len;
  200. }
  201. if (des) (*des) = 0;
  202. return num;
  203. }
  204. template <typename T, typename U>
  205. typename enable_if<(check<T, 1>::value || check<T, 2>::value) && check<U, 4>::value,
  206. size_t>::type_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-32
  207. {
  208. if (!src || (*src) == 0) return 0;
  209. size_t num = 0;
  210. while(*src)
  211. {
  212. uint32 tmp;
  213. size_t len = utf(src, tmp);
  214. if (len == 0) break;
  215. if (des)
  216. {
  217. (*des) = tmp;
  218. ++des;
  219. }
  220. src += len;
  221. num += 1;
  222. }
  223. if (des) (*des) = 0;
  224. return num;
  225. }
  226. template <typename T, typename U>
  227. typename enable_if<(check<T, 1>::value && check<U, 2>::value) ||
  228. (check<T, 2>::value && check<U, 1>::value),
  229. size_t>::type_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-Y(16/8)
  230. {
  231. if (!src || (*src) == 0) return 0;
  232. size_t num = 0;
  233. while(*src)
  234. {
  235. // make utf-x to utf-32
  236. uint32 tmp;
  237. size_t len = utf(src, tmp);
  238. if (len == 0) break;
  239. src += len;
  240. // make utf-32 to utf-y
  241. len = utf(tmp, des);
  242. if (len == 0) break;
  243. if (des) des += len;
  244. num += len;
  245. }
  246. if (des) (*des) = 0;
  247. return num;
  248. }
  249. }

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

【转】UTF-8、UTF-16、UTF-32编码的相互转换插图

微信扫一扫 关注公众号

微信扫一扫 使用小程序

百度扫一扫 使用小程序