UTF-8编码的字符串,不同的字符长度不一样。和Unicode作为对比,Unicode的字符串每个字符的长度都是固定的,比如16位的Unicode(也就是Windows使用的这种)它就是2个字节一个字符(即便是半角字母)的固定长度字符数组。
UTF-8是一种针对Unicode的可变长度字元编码,也是一种前缀码。它可以用来表示Unicode标准中的任何字元。[参考]
在Unicode字符值小于128的时候,UTF-8可以只用一个字节来表示一个字符,这点可以做到和ASCII兼容。因此,这使得原来处理ASCII字元的软件无须或只须做少部分修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或发送文字的应用中,优先采用的编码。[参考]
使用UTF-8编码的字符串,其中的字符的长度取决于这个字符的值。我们可以用下面这张表很直观地将编码方式表示出来。
| 字符的位数范围 | 开始值 | 结束值 | UTF-8字节数 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | | 0-7 | 0x00 | 0x7F | 1 | 0xxxxxxx | | 8-11 | 0x80 | 0x7FF | 2 | 110xxxxx | 10xxxxxx | | 12-16 | 0x800 | 0xFFFF | 3 | 1110xxxx | 10xxxxxx | 10xxxxxx | | 17-21 | 0x10000 | 0x1FFFFF | 4 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | | 22-26 | 0x200000 | 0x3FFFFFF | 5 | 111110xx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | | 27-31 | 0x4000000 | 0x7FFFFFFF | 6 | 1111110x | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
其中的“xxxxxx”就是原字符的二进制位了。UTF-8编码将Unicode字符值的高位放在靠前的字节,而低位则放在靠后的字节中。
知道了以上的信息之后,你大概也知道怎么写Unicode到UTF-8之间互转的函数了吧?
这里放出我写好的库,它不使用API,直接就能编码、解码UTF-8的字符串为Unicode了。//=============================================================================
//作者:0xAA55
//作者网站:http://www.0xaa55.com/
//本源码完全免费开放,大家可以随意拿去使用、修改、用于任何用途。
//但是,由于本源码带来的任何损失,均与本作者无关。请保留此信息。
//
//参考资料:
//RFC 3629
//https://zh.wikipedia.org/wiki/UTF-8
//http://www.0xaa55.com/thread-1676-1-1.html
//-----------------------------------------------------------------------------
#include"wcs2utf8.h"
//=============================================================================
//函数:_u16toutf8
//描述:将16位Unicode编码的字符串转换为UTF-8
// 返回转换为UTF-8后的字符串的长度。pUTF8可为NULL,表示不接收转换好的字符串
//-----------------------------------------------------------------------------
size_t _u16toutf8
(
char*pUTF8,
const uint16_t*pwStr,
size_t count
)
{
size_t cbUTF8 = 0;
size_t i;
char*pChr;
//pUTF8为NULL时,返回存储UTF-8字符串所需字节数
if(!pUTF8)
{
for(i=0;i<count;i++)
{
if(pwStr[i] >= 0x0800)
cbUTF8 += 3;
else if(pwStr[i] >= 0x0080)
cbUTF8 += 2;
else
cbUTF8++;
}
return cbUTF8;
}
//输出UTF-8编码的字符串
pChr = pUTF8;
for(i=0;i<count;i++)
{
if(pwStr[i] >= 0x0800)
{
*pChr++ = 0xE0 | ((pwStr[i] >> 12) & 0x0F);
*pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 3;
}
else if(pwStr[i] >= 0x0080)
{
*pChr++ = 0xC0 | ((pwStr[i] >> 6) & 0x1F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 2;
}
else
{
*pChr++ = (char)pwStr[i];
cbUTF8++;
}
}
return cbUTF8;
}
//=============================================================================
//函数:_u32toutf8
//描述:将32位Unicode编码的字符串转换为UTF-8
// 返回转换为UTF-8后的字符串的长度。pUTF8可为NULL,表示不接收转换好的字符串
//-----------------------------------------------------------------------------
size_t _u32toutf8
(
char*pUTF8,
const uint32_t*pwStr,
size_t count
)
{
size_t cbUTF8 = 0;
size_t i;
char*pChr;
//pUTF8为NULL时,返回存储UTF-8字符串所需字节数
if(!pUTF8)
{
for(i=0;i<count;i++)
{
if(pwStr[i] >= 0x4000000)
cbUTF8 += 6;
else if(pwStr[i] >= 0x200000)
cbUTF8 += 5;
else if(pwStr[i] >= 0x10000)
cbUTF8 += 4;
else if(pwStr[i] >= 0x0800)
cbUTF8 += 3;
else if(pwStr[i] >= 0x0080)
cbUTF8 += 2;
else
cbUTF8++;
}
return cbUTF8;
}
//输出UTF-8编码的字符串
pChr = pUTF8;
for(i=0;i<count;i++)
{
if(pwStr[i] >= 0x4000000)
{
*pChr++ = 0xFC | ((pwStr[i] >> 30) & 0x01);
*pChr++ = 0x80 | ((pwStr[i] >> 24) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 18) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 6;
}
else if(pwStr[i] >= 0x200000)
{
*pChr++ = 0xF8 | ((pwStr[i] >> 24) & 0x03);
*pChr++ = 0x80 | ((pwStr[i] >> 18) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 5;
}
else if(pwStr[i] >= 0x10000)
{
*pChr++ = 0xF0 | ((pwStr[i] >> 18) & 0x07);
*pChr++ = 0x80 | ((pwStr[i] >> 12) & 0x3F);
*pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 4;
}
else if(pwStr[i] >= 0x0800)
{
*pChr++ = 0xE0 | ((pwStr[i] >> 12) & 0x0F);
*pChr++ = 0x80 | ((pwStr[i] >> 6) & 0x3F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 3;
}
else if(pwStr[i] >= 0x0080)
{
*pChr++ = 0xC0 | ((pwStr[i] >> 6) & 0x1F);
*pChr++ = 0x80 | (pwStr[i] & 0x3F);
cbUTF8 += 2;
}
else
{
*pChr++ = (char)pwStr[i];
cbUTF8++;
}
}
return cbUTF8;
}
//=============================================================================
//函数:_GetUTF8NbWChars
//描述:取得UTF-8字符串转换为Unicode编码的字符串的字符数
// 失败返回0
//-----------------------------------------------------------------------------
static size_t _GetUTF8NbWChars(const char*pUTF8,size_t cb)
{
size_t cchUnicode = 0;
size_t i;
for(i=0;i<cb;)
{
if((pUTF8[i] & 0xFE) == 0xFC)
{
cchUnicode++;
i += 6;
}
else if((pUTF8[i] & 0xFC) == 0xF8)
{
cchUnicode++;
i += 5;
}
else if((pUTF8[i] & 0xF8) == 0xF0)
{
cchUnicode++;
i += 4;
}
else if((pUTF8[i] & 0xF0) == 0xE0)
{
cchUnicode++;
i += 3;
}
else if((pUTF8[i] & 0xE0) == 0xC0)
{
cchUnicode++;
i += 2;
}
else if((pUTF8[i] & 0xC0) == 0x80)
{
//遇到高2位是10的字符,这是不应该出现的。
return 0;
}
else if((pUTF8[i] & 0x80) == 0x00)
{
cchUnicode++;
i++;
}
}
//字符串不完整
if(i > cb)
cchUnicode--;
return cchUnicode;
}
//=============================================================================
//函数:_utf8tou16
//描述:将UTF-8编码的字符串转换为16位Unicode
// 返回转换为Unicode后的字符串的字符数。pUnicode可为NULL,表示不接收字符串。
// 注意失败返回0
//-----------------------------------------------------------------------------
size_t _utf8tou16
(
uint16_t*pUnicode,
const char*pUTF8,
size_t cb
)
{
size_t cchUnicode = 0;
size_t i;
uint16_t*pChr;
if(!pUnicode)
return _GetUTF8NbWChars(pUTF8,cb);
pChr = pUnicode;
for(i=0;i<cb;)
{
if( (pUTF8[i] & 0xFE) == 0xFC ||
(pUTF8[i] & 0xFC) == 0xF8 ||
(pUTF8[i] & 0xF8) == 0xF0)
{
//遇到32位Unicode字符,停止。本函数不包这服务。
return 0;
}
else if((pUTF8[i] & 0xF0) == 0xE0)
{
if(i + 3 <= cb)
{
*pChr++ =
(((uint16_t)pUTF8[i+0] & 0x0F) << 12)|
(((uint16_t)pUTF8[i+1] & 0x3F) << 6)|
(((uint16_t)pUTF8[i+2] & 0x3F) << 0);
cchUnicode++;
i += 3;
}
else
break;
}
else if((pUTF8[i] & 0xE0) == 0xC0)
{
if(i + 2 <= cb)
{
*pChr++ =
(((uint16_t)pUTF8[i+0] & 0x1F) << 6)|
(((uint16_t)pUTF8[i+1] & 0x3F) << 0);
cchUnicode++;
i += 2;
}
else
break;
}
else if((pUTF8[i] & 0xC0) == 0x80)
{
//遇到高2位是10的字符,这是不应该出现的。
return 0;
}
else if((pUTF8[i] & 0x80) == 0x00)
{
*pChr++ = pUTF8[i] & 0x7F;
cchUnicode++;
i++;
}
}
return cchUnicode;
}
//=============================================================================
//函数:_utf8tou32
//描述:将UTF-8编码的字符串转换为32位Unicode
// 返回转换为Unicode后的字符串的字符数。pUnicode可为NULL,表示不接收字符串。
// 注意失败返回0
//-----------------------------------------------------------------------------
size_t _utf8tou32
(
uint32_t*pUnicode,
const char*pUTF8,
size_t cb
)
{
size_t cchUnicode = 0;
size_t i;
uint32_t*pChr;
if(!pUnicode)
return _GetUTF8NbWChars(pUTF8,cb);
pChr = pUnicode;
for(i=0;i<cb;)
{
if((pUTF8[i] & 0xFE) == 0xFC)//1111110x
{
if(i + 6 <= cb)
{
*pChr++ =
(((uint32_t)pUTF8[i+0] & 0x01) << 30)|
(((uint32_t)pUTF8[i+1] & 0x3F) << 24)|
(((uint32_t)pUTF8[i+2] & 0x3F) << 18)|
(((uint32_t)pUTF8[i+3] & 0x3F) << 12)|
(((uint32_t)pUTF8[i+4] & 0x3F) << 6)|
(((uint32_t)pUTF8[i+5] & 0x3F) << 0);
cchUnicode++;
i += 6;
}
else
break;
}
else if((pUTF8[i] & 0xFC) == 0xF8)//111110xx
{
if(i + 5 <= cb)
{
*pChr++ =
(((uint32_t)pUTF8[i+0] & 0x03) << 24)|
(((uint32_t)pUTF8[i+1] & 0x3F) << 18)|
(((uint32_t)pUTF8[i+2] & 0x3F) << 12)|
(((uint32_t)pUTF8[i+3] & 0x3F) << 6)|
(((uint32_t)pUTF8[i+4] & 0x3F) << 0);
cchUnicode++;
i += 5;
}
else
break;
}
else if((pUTF8[i] & 0xF8) == 0xF0)//11110xxx
{
if(i + 4 <= cb)
{
*pChr++ =
(((uint32_t)pUTF8[i+0] & 0x07) << 18)|
(((uint32_t)pUTF8[i+1] & 0x3F) << 12)|
(((uint32_t)pUTF8[i+2] & 0x3F) << 6)|
(((uint32_t)pUTF8[i+3] & 0x3F) << 0);
cchUnicode++;
i += 4;
}
else
break;
}
else if((pUTF8[i] & 0xF0) == 0xE0)//1110xxxx
{
if(i + 3 <= cb)
{
*pChr++ =
(((uint32_t)pUTF8[i+0] & 0x0F) << 12)|
(((uint32_t)pUTF8[i+1] & 0x3F) << 6)|
(((uint32_t)pUTF8[i+2] & 0x3F) << 0);
cchUnicode++;
i += 3;
}
else
break;
}
else if((pUTF8[i] & 0xE0) == 0xC0)//110xxxxx
{
if(i + 2 <= cb)
{
*pChr++ =
(((uint32_t)pUTF8[i+0] & 0x1F) << 6)|
(((uint32_t)pUTF8[i+1] & 0x3F) << 0);
cchUnicode++;
i += 2;
}
else
break;
}
else if((pUTF8[i] & 0xC0) == 0x80)//10xxxxxx
{
//遇到高2位是10的字符,这是不应该出现的。
return 0;
}
else if((pUTF8[i] & 0x80) == 0x00)//0xxxxxxx
{
*pChr++ = pUTF8[i] & 0x7F;
cchUnicode++;
i++;
}
}
return cchUnicode;
}
测试代码:#include"wcs2utf8.h"
#include<stdio.h>
#include<locale.h>
#include<malloc.h>
#include<string.h>
#include<Windows.h>
int main(int argc,char**argv)
{
char*pszUTF8;
size_t cbUTF8;
//要注意wchar_t的大小不一定是2
wchar_t wstr[] = L"蛤蛤蛤!我是wchar_t字符串!\n";
size_t cchwstr = wcslen(wstr);
wchar_t wcBuf[999] = {0};//测试用缓冲区
//不加这个不能显示Unicode字符串
setlocale(LC_ALL, ".ACP");
//显示字符串
fputs("这是原始的字符串:\n",stderr);
fputws(wstr,stdout);
//用自己的函数转换字符串
cbUTF8 = _u16toutf8(NULL,(uint16_t*)wstr,cchwstr);
pszUTF8 = (char*)malloc(cbUTF8);//没有'\0'结尾
if(!pszUTF8)
{
fputs("额..\n",stderr);
return 1;
}
_u16toutf8(pszUTF8,(uint16_t*)wstr,cchwstr);
//然后用WinAPI测试它对不对
MultiByteToWideChar(CP_UTF8, 0, pszUTF8, cbUTF8, wcBuf, 999);
//显示字符串
fputs("这是用MultiByteToWideChar把UTF-8字符串转换成Unicode的字符串:\n",stderr);
fputws(wcBuf,stdout);
//然后我们再测试用自己的函数将UTF-8字符串转换为Unicode
_utf8tou16((uint16_t*)wcBuf, pszUTF8, cbUTF8);
fputs("这是用_utf8tou16把UTF-8字符串转换成Unicode的字符串:\n",stderr);
fputws(wcBuf,stdout);
free(pszUTF8);
return 0;
}
完整源码下载:
utf8test.7z
(3.69 KB, 下载次数: 0, 售价: 4 个宅币)

相关参考资料:
RFC 3629
https://zh.wikipedia.org/wiki/UTF-8 |