找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 5694|回复: 4

【C】字符串编码UTF-8的详细资料和编码方式

[复制链接]
发表于 2016-1-2 08:02:51 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
UTF-8编码的字符串,不同的字符长度不一样。和Unicode作为对比,Unicode的字符串每个字符的长度都是固定的,比如16位的Unicode(也就是Windows使用的这种)它就是2个字节一个字符(即便是半角字母)的固定长度字符数组。
UTF-8是一种针对Unicode的可变长度字元编码,也是一种前缀码。它可以用来表示Unicode标准中的任何字元。[参考]
在Unicode字符值小于128的时候,UTF-8可以只用一个字节来表示一个字符,这点可以做到和ASCII兼容。因此,这使得原来处理ASCII字元的软件无须或只须做少部分修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或发送文字的应用中,优先采用的编码。[参考]

使用UTF-8编码的字符串,其中的字符的长度取决于这个字符的值。我们可以用下面这张表很直观地将编码方式表示出来。
字符的位数范围开始值结束值UTF-8字节数Byte 1Byte 2Byte 3Byte 4Byte 5Byte 6
0-70x000x7F10xxxxxxx
8-110x800x7FF2110xxxxx10xxxxxx
12-160x8000xFFFF31110xxxx10xxxxxx10xxxxxx
17-210x100000x1FFFFF411110xxx10xxxxxx10xxxxxx10xxxxxx
22-260x2000000x3FFFFFF5111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxx
27-310x40000000x7FFFFFFF61111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx

其中的“xxxxxx”就是原字符的二进制位了。UTF-8编码将Unicode字符值的高位放在靠前的字节,而低位则放在靠后的字节中。
知道了以上的信息之后,你大概也知道怎么写Unicode到UTF-8之间互转的函数了吧?
这里放出我写好的库,它不使用API,直接就能编码、解码UTF-8的字符串为Unicode了。
  1. //=============================================================================
  2. //作者:0xAA55
  3. //作者网站:http://www.0xaa55.com/
  4. //本源码完全免费开放,大家可以随意拿去使用、修改、用于任何用途。
  5. //但是,由于本源码带来的任何损失,均与本作者无关。请保留此信息。
  6. //
  7. //参考资料:
  8. //RFC 3629
  9. //https://zh.wikipedia.org/wiki/UTF-8
  10. //http://www.0xaa55.com/thread-1676-1-1.html
  11. //-----------------------------------------------------------------------------
  12. #include"wcs2utf8.h"

  13. //=============================================================================
  14. //函数:_u16toutf8
  15. //描述:将16位Unicode编码的字符串转换为UTF-8
  16. //    返回转换为UTF-8后的字符串的长度。pUTF8可为NULL,表示不接收转换好的字符串
  17. //-----------------------------------------------------------------------------
  18. size_t _u16toutf8
  19. (
  20.         char*pUTF8,
  21.         const uint16_t*pwStr,
  22.         size_t count
  23. )
  24. {
  25.         size_t cbUTF8 = 0;
  26.         size_t i;
  27.         char*pChr;

  28.         //pUTF8为NULL时,返回存储UTF-8字符串所需字节数
  29.         if(!pUTF8)
  30.         {
  31.                 for(i=0;i<count;i++)
  32.                 {
  33.                         if(pwStr[i] >= 0x0800)
  34.                                 cbUTF8 += 3;
  35.                         else if(pwStr[i] >= 0x0080)
  36.                                 cbUTF8 += 2;
  37.                         else
  38.                                 cbUTF8++;
  39.                 }
  40.                 return cbUTF8;
  41.         }
  42.        
  43.         //输出UTF-8编码的字符串
  44.         pChr = pUTF8;
  45.         for(i=0;i<count;i++)
  46.         {
  47.                 if(pwStr[i] >= 0x0800)
  48.                 {
  49.                         *pChr++ = 0xE0 | ((pwStr[i] >> 12) & 0x0F);
  50.                         *pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
  51.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  52.                         cbUTF8 += 3;
  53.                 }
  54.                 else if(pwStr[i] >= 0x0080)
  55.                 {
  56.                         *pChr++ = 0xC0 | ((pwStr[i] >> 6) & 0x1F);
  57.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  58.                         cbUTF8 += 2;
  59.                 }
  60.                 else
  61.                 {
  62.                         *pChr++ = (char)pwStr[i];
  63.                         cbUTF8++;
  64.                 }
  65.         }
  66.         return cbUTF8;
  67. }

  68. //=============================================================================
  69. //函数:_u32toutf8
  70. //描述:将32位Unicode编码的字符串转换为UTF-8
  71. //    返回转换为UTF-8后的字符串的长度。pUTF8可为NULL,表示不接收转换好的字符串
  72. //-----------------------------------------------------------------------------
  73. size_t _u32toutf8
  74. (
  75.         char*pUTF8,
  76.         const uint32_t*pwStr,
  77.         size_t count
  78. )
  79. {
  80.         size_t cbUTF8 = 0;
  81.         size_t i;
  82.         char*pChr;

  83.         //pUTF8为NULL时,返回存储UTF-8字符串所需字节数
  84.         if(!pUTF8)
  85.         {
  86.                 for(i=0;i<count;i++)
  87.                 {
  88.                         if(pwStr[i] >= 0x4000000)
  89.                                 cbUTF8 += 6;
  90.                         else if(pwStr[i] >= 0x200000)
  91.                                 cbUTF8 += 5;
  92.                         else if(pwStr[i] >= 0x10000)
  93.                                 cbUTF8 += 4;
  94.                         else if(pwStr[i] >= 0x0800)
  95.                                 cbUTF8 += 3;
  96.                         else if(pwStr[i] >= 0x0080)
  97.                                 cbUTF8 += 2;
  98.                         else
  99.                                 cbUTF8++;
  100.                 }
  101.                 return cbUTF8;
  102.         }
  103.        
  104.         //输出UTF-8编码的字符串
  105.         pChr = pUTF8;
  106.         for(i=0;i<count;i++)
  107.         {
  108.                 if(pwStr[i] >= 0x4000000)
  109.                 {
  110.                         *pChr++ = 0xFC | ((pwStr[i] >> 30) & 0x01);
  111.                         *pChr++ = 0x80 | ((pwStr[i] >> 24) & 0x3F);
  112.                         *pChr++ = 0x80 | ((pwStr[i] >> 18) & 0x3F);
  113.                         *pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
  114.                         *pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
  115.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  116.                         cbUTF8 += 6;
  117.                 }
  118.                 else if(pwStr[i] >= 0x200000)
  119.                 {
  120.                         *pChr++ = 0xF8 | ((pwStr[i] >> 24) & 0x03);
  121.                         *pChr++ = 0x80 | ((pwStr[i] >> 18) & 0x3F);
  122.                         *pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
  123.                         *pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
  124.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  125.                         cbUTF8 += 5;
  126.                 }
  127.                 else if(pwStr[i] >= 0x10000)
  128.                 {
  129.                         *pChr++ = 0xF0 | ((pwStr[i] >> 18) & 0x07);
  130.                         *pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
  131.                         *pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
  132.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  133.                         cbUTF8 += 4;
  134.                 }
  135.                 else if(pwStr[i] >= 0x0800)
  136.                 {
  137.                         *pChr++ = 0xE0 | ((pwStr[i] >> 12) & 0x0F);
  138.                         *pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
  139.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  140.                         cbUTF8 += 3;
  141.                 }
  142.                 else if(pwStr[i] >= 0x0080)
  143.                 {
  144.                         *pChr++ = 0xC0 | ((pwStr[i] >> 6) & 0x1F);
  145.                         *pChr++ = 0x80 | (pwStr[i] & 0x3F);
  146.                         cbUTF8 += 2;
  147.                 }
  148.                 else
  149.                 {
  150.                         *pChr++ = (char)pwStr[i];
  151.                         cbUTF8++;
  152.                 }
  153.         }
  154.         return cbUTF8;
  155. }

  156. //=============================================================================
  157. //函数:_GetUTF8NbWChars
  158. //描述:取得UTF-8字符串转换为Unicode编码的字符串的字符数
  159. //    失败返回0
  160. //-----------------------------------------------------------------------------
  161. static size_t _GetUTF8NbWChars(const char*pUTF8,size_t cb)
  162. {
  163.         size_t cchUnicode = 0;
  164.         size_t i;

  165.         for(i=0;i<cb;)
  166.         {
  167.                 if((pUTF8[i] & 0xFE) == 0xFC)
  168.                 {
  169.                         cchUnicode++;
  170.                         i += 6;
  171.                 }
  172.                 else if((pUTF8[i] & 0xFC) == 0xF8)
  173.                 {
  174.                         cchUnicode++;
  175.                         i += 5;
  176.                 }
  177.                 else if((pUTF8[i] & 0xF8) == 0xF0)
  178.                 {
  179.                         cchUnicode++;
  180.                         i += 4;
  181.                 }
  182.                 else if((pUTF8[i] & 0xF0) == 0xE0)
  183.                 {
  184.                         cchUnicode++;
  185.                         i += 3;
  186.                 }
  187.                 else if((pUTF8[i] & 0xE0) == 0xC0)
  188.                 {
  189.                         cchUnicode++;
  190.                         i += 2;
  191.                 }
  192.                 else if((pUTF8[i] & 0xC0) == 0x80)
  193.                 {
  194.                         //遇到高2位是10的字符,这是不应该出现的。
  195.                         return 0;
  196.                 }
  197.                 else if((pUTF8[i] & 0x80) == 0x00)
  198.                 {
  199.                         cchUnicode++;
  200.                         i++;
  201.                 }
  202.         }

  203.         //字符串不完整
  204.         if(i > cb)
  205.                 cchUnicode--;

  206.         return cchUnicode;
  207. }

  208. //=============================================================================
  209. //函数:_utf8tou16
  210. //描述:将UTF-8编码的字符串转换为16位Unicode
  211. //    返回转换为Unicode后的字符串的字符数。pUnicode可为NULL,表示不接收字符串。
  212. //    注意失败返回0
  213. //-----------------------------------------------------------------------------
  214. size_t _utf8tou16
  215. (
  216.         uint16_t*pUnicode,
  217.         const char*pUTF8,
  218.         size_t cb
  219. )
  220. {
  221.         size_t cchUnicode = 0;
  222.         size_t i;
  223.         uint16_t*pChr;

  224.         if(!pUnicode)
  225.                 return _GetUTF8NbWChars(pUTF8,cb);

  226.         pChr = pUnicode;
  227.         for(i=0;i<cb;)
  228.         {
  229.                 if( (pUTF8[i] & 0xFE) == 0xFC ||
  230.                         (pUTF8[i] & 0xFC) == 0xF8 ||
  231.                         (pUTF8[i] & 0xF8) == 0xF0)
  232.                 {
  233.                         //遇到32位Unicode字符,停止。本函数不包这服务。
  234.                         return 0;
  235.                 }
  236.                 else if((pUTF8[i] & 0xF0) == 0xE0)
  237.                 {
  238.                         if(i + 3 <= cb)
  239.                         {
  240.                                 *pChr++ =
  241.                                         (((uint16_t)pUTF8[i+0] & 0x0F) << 12)|
  242.                                         (((uint16_t)pUTF8[i+1] & 0x3F) << 6)|
  243.                                         (((uint16_t)pUTF8[i+2] & 0x3F) << 0);
  244.                                 cchUnicode++;
  245.                                 i += 3;
  246.                         }
  247.                         else
  248.                                 break;
  249.                 }
  250.                 else if((pUTF8[i] & 0xE0) == 0xC0)
  251.                 {
  252.                         if(i + 2 <= cb)
  253.                         {
  254.                                 *pChr++ =
  255.                                         (((uint16_t)pUTF8[i+0] & 0x1F) << 6)|
  256.                                         (((uint16_t)pUTF8[i+1] & 0x3F) << 0);
  257.                                 cchUnicode++;
  258.                                 i += 2;
  259.                         }
  260.                         else
  261.                                 break;
  262.                 }
  263.                 else if((pUTF8[i] & 0xC0) == 0x80)
  264.                 {
  265.                         //遇到高2位是10的字符,这是不应该出现的。
  266.                         return 0;
  267.                 }
  268.                 else if((pUTF8[i] & 0x80) == 0x00)
  269.                 {
  270.                         *pChr++ = pUTF8[i] & 0x7F;
  271.                         cchUnicode++;
  272.                         i++;
  273.                 }
  274.         }
  275.         return cchUnicode;
  276. }


  277. //=============================================================================
  278. //函数:_utf8tou32
  279. //描述:将UTF-8编码的字符串转换为32位Unicode
  280. //    返回转换为Unicode后的字符串的字符数。pUnicode可为NULL,表示不接收字符串。
  281. //    注意失败返回0
  282. //-----------------------------------------------------------------------------
  283. size_t _utf8tou32
  284. (
  285.         uint32_t*pUnicode,
  286.         const char*pUTF8,
  287.         size_t cb
  288. )
  289. {
  290.         size_t cchUnicode = 0;
  291.         size_t i;
  292.         uint32_t*pChr;

  293.         if(!pUnicode)
  294.                 return _GetUTF8NbWChars(pUTF8,cb);

  295.         pChr = pUnicode;
  296.         for(i=0;i<cb;)
  297.         {
  298.                 if((pUTF8[i] & 0xFE) == 0xFC)//1111110x
  299.                 {
  300.                         if(i + 6 <= cb)
  301.                         {
  302.                                 *pChr++ =
  303.                                         (((uint32_t)pUTF8[i+0] & 0x01) << 30)|
  304.                                         (((uint32_t)pUTF8[i+1] & 0x3F) << 24)|
  305.                                         (((uint32_t)pUTF8[i+2] & 0x3F) << 18)|
  306.                                         (((uint32_t)pUTF8[i+3] & 0x3F) << 12)|
  307.                                         (((uint32_t)pUTF8[i+4] & 0x3F) << 6)|
  308.                                         (((uint32_t)pUTF8[i+5] & 0x3F) << 0);
  309.                                 cchUnicode++;
  310.                                 i += 6;
  311.                         }
  312.                         else
  313.                                 break;
  314.                 }
  315.                 else if((pUTF8[i] & 0xFC) == 0xF8)//111110xx
  316.                 {
  317.                         if(i + 5 <= cb)
  318.                         {
  319.                                 *pChr++ =
  320.                                         (((uint32_t)pUTF8[i+0] & 0x03) << 24)|
  321.                                         (((uint32_t)pUTF8[i+1] & 0x3F) << 18)|
  322.                                         (((uint32_t)pUTF8[i+2] & 0x3F) << 12)|
  323.                                         (((uint32_t)pUTF8[i+3] & 0x3F) << 6)|
  324.                                         (((uint32_t)pUTF8[i+4] & 0x3F) << 0);
  325.                                 cchUnicode++;
  326.                                 i += 5;
  327.                         }
  328.                         else
  329.                                 break;
  330.                 }
  331.                 else if((pUTF8[i] & 0xF8) == 0xF0)//11110xxx
  332.                 {
  333.                         if(i + 4 <= cb)
  334.                         {
  335.                                 *pChr++ =
  336.                                         (((uint32_t)pUTF8[i+0] & 0x07) << 18)|
  337.                                         (((uint32_t)pUTF8[i+1] & 0x3F) << 12)|
  338.                                         (((uint32_t)pUTF8[i+2] & 0x3F) << 6)|
  339.                                         (((uint32_t)pUTF8[i+3] & 0x3F) << 0);
  340.                                 cchUnicode++;
  341.                                 i += 4;
  342.                         }
  343.                         else
  344.                                 break;
  345.                 }
  346.                 else if((pUTF8[i] & 0xF0) == 0xE0)//1110xxxx
  347.                 {
  348.                         if(i + 3 <= cb)
  349.                         {
  350.                                 *pChr++ =
  351.                                         (((uint32_t)pUTF8[i+0] & 0x0F) << 12)|
  352.                                         (((uint32_t)pUTF8[i+1] & 0x3F) << 6)|
  353.                                         (((uint32_t)pUTF8[i+2] & 0x3F) << 0);
  354.                                 cchUnicode++;
  355.                                 i += 3;
  356.                         }
  357.                         else
  358.                                 break;
  359.                 }
  360.                 else if((pUTF8[i] & 0xE0) == 0xC0)//110xxxxx
  361.                 {
  362.                         if(i + 2 <= cb)
  363.                         {
  364.                                 *pChr++ =
  365.                                         (((uint32_t)pUTF8[i+0] & 0x1F) << 6)|
  366.                                         (((uint32_t)pUTF8[i+1] & 0x3F) << 0);
  367.                                 cchUnicode++;
  368.                                 i += 2;
  369.                         }
  370.                         else
  371.                                 break;
  372.                 }
  373.                 else if((pUTF8[i] & 0xC0) == 0x80)//10xxxxxx
  374.                 {
  375.                         //遇到高2位是10的字符,这是不应该出现的。
  376.                         return 0;
  377.                 }
  378.                 else if((pUTF8[i] & 0x80) == 0x00)//0xxxxxxx
  379.                 {
  380.                         *pChr++ = pUTF8[i] & 0x7F;
  381.                         cchUnicode++;
  382.                         i++;
  383.                 }
  384.         }
  385.         return cchUnicode;
  386. }
复制代码
测试代码:
  1. #include"wcs2utf8.h"

  2. #include<stdio.h>
  3. #include<locale.h>
  4. #include<malloc.h>
  5. #include<string.h>
  6. #include<Windows.h>

  7. int main(int argc,char**argv)
  8. {
  9.         char*pszUTF8;
  10.         size_t cbUTF8;
  11.         //要注意wchar_t的大小不一定是2
  12.         wchar_t wstr[] = L"蛤蛤蛤!我是wchar_t字符串!\n";
  13.         size_t cchwstr = wcslen(wstr);
  14.         wchar_t wcBuf[999] = {0};//测试用缓冲区

  15.         //不加这个不能显示Unicode字符串
  16.         setlocale(LC_ALL, ".ACP");

  17.         //显示字符串
  18.         fputs("这是原始的字符串:\n",stderr);
  19.         fputws(wstr,stdout);

  20.         //用自己的函数转换字符串
  21.         cbUTF8 = _u16toutf8(NULL,(uint16_t*)wstr,cchwstr);
  22.         pszUTF8 = (char*)malloc(cbUTF8);//没有'\0'结尾
  23.         if(!pszUTF8)
  24.         {
  25.                 fputs("额..\n",stderr);
  26.                 return 1;
  27.         }
  28.         _u16toutf8(pszUTF8,(uint16_t*)wstr,cchwstr);

  29.         //然后用WinAPI测试它对不对
  30.         MultiByteToWideChar(CP_UTF8, 0, pszUTF8, cbUTF8, wcBuf, 999);
  31.        
  32.         //显示字符串
  33.         fputs("这是用MultiByteToWideChar把UTF-8字符串转换成Unicode的字符串:\n",stderr);
  34.         fputws(wcBuf,stdout);

  35.         //然后我们再测试用自己的函数将UTF-8字符串转换为Unicode
  36.         _utf8tou16((uint16_t*)wcBuf, pszUTF8, cbUTF8);
  37.         fputs("这是用_utf8tou16把UTF-8字符串转换成Unicode的字符串:\n",stderr);
  38.         fputws(wcBuf,stdout);
  39.                
  40.         free(pszUTF8);
  41.         return 0;
  42. }
复制代码
20160102085720.png
完整源码下载: utf8test.7z (3.69 KB, 下载次数: 0, 售价: 4 个宅币)


相关参考资料:


RFC 3629
https://zh.wikipedia.org/wiki/UTF-8

本帖被以下淘专辑推荐:

回复

使用道具 举报

 楼主| 发表于 2016-1-16 16:26:46 | 显示全部楼层
总结:
1、UTF-8其实就是针对Unicode的一种纯粹的“压缩算法”。对于[0x01, 0x7F]区间内的所有Unicode字符,可以被UTF-8编码成兼容ASCII码的7-bit字节串,并且还能减少字符串的存储体积。
2、很多网页能在不同国家、不同语言、不同系统的环境中显示出正确的字符而不是乱码,其实是因为它使用了UTF-8编码,实质上的效果相当于网页是用Unicode编码的。跨语言的编码正是Unicode。
回复 赞! 靠!

使用道具 举报

发表于 2016-5-7 07:27:42 来自手机 | 显示全部楼层
以前竟然一直以为Unicode和UTF-8是同一种编码
回复 赞! 靠!

使用道具 举报

发表于 2017-10-11 22:47:44 | 显示全部楼层
编码这东西有时候真的让人很头痛
回复 赞! 靠!

使用道具 举报

发表于 2020-12-4 10:28:56 | 显示全部楼层
感谢分享。。。
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-23 17:55 , Processed in 0.039476 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表