待总结: Base64Encode; UrlEncode
urlencode:https://www.cnblogs.com/yingsong/p/8884692.html https://www.cnblogs.com/w-essay/p/8615759.html https://segmentfault.com/q/1010000002991580 https://www.cnblogs.com/takako_mu/p/3548010.html
https://www.cnblogs.com/ranjiewen/p/5770639.html
目录
5.Unicode Character Set与Multi-Byte Character Set的区别
5.1 c++基本数据类型中表示整数、字符和布尔的算术类型合称整型。
5.3 Unicode Character Set与Multi-Byte Character Set有什么区别呢?
6. ANSI,Unicode,utf-8,string之间的转换
1. ASCII码:
1.1产生
1.2 表达方式
1.3 大小规则
2. 字符集和字符编码
http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html
编码:字符->二进制;解码:二进制->字符
几种编码:
参考: https://www.jianshu.com/p/ed948590a3af 几种字符编码
https://www.cnblogs.com/dedema/p/10851740.html 字符编码ANSI和ASCII区别、Unicode和UTF-8区别
https://www.jianshu.com/p/1f36bc2a247f 字符编码ASCII、ANSI、Unicode、UTF-8、GB2312
2.1.ASCII
计算机只能处理0和1,所有的文本都要转换成数字才能处理。早期计算机在设计时采用8个比特(bit)为一个字节,计算机是由美国人发明的,他们将26个字母、数字和一些符号编码到计算机中。一个字节(8位)表示,最多能表示128种字符,称为ASCII码。
2.2.ANSI
ANSI编码是一种对ASCII码的拓展:ANSI编码用0x00~0x7f (即十进制下的0到127)范围的1 个字节来表示 1 个英文字符,超出一个字节的 0x80~0xFFFF 范围来表示其他语言的其他字符。也就是说,ANSI码仅在前128(0-127)个与ASCII码相同,之后的字符全是某个国家语言的所有字符。值得注意的是,两个字节最多可以存储的字符数目是2的16次方,即65536个字符,这对于一个语言的字符来说,绝对够了。还有ANSI编码其实包括很多编码:中国制定了GB2312编码,用来把中文编进去另外,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准。受制于当时的条件,不同语言之间的ANSI码之间不能互相转换,这就会导致在多语言混合的文本中会有乱码。
不同的国家和地区制定了不同的标准,由此产生了 GB2312(中国), BIG5(台湾), JIS(日本) 等各自的编码标准。 这些使用 2 个字节来代表一个字符的各种延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 不同 ANSI 编码之间互不兼容。
2.3.Unicode
非英语国家只能规定适用自己语言的编码,如中国制定了GB2312
编码,日本制定了Shift_JIS
编码,韩国制定了Euc-kr
编码。如此以来,各国执行不同的标准,如果文本同时出现多种语言,则无法正常显示,会有乱码。
所以Unicode应运而生,它把所有语言都统一到一套编码里,这样就不会出现显示乱码问题了。
上面说到,ASCII采用1个字节编码,Unicode通常采用2个字节编码,所以最大能表示65536种字符。
字母A
用ASCII码表示是十进制的65
,二进制中是`0100 0001
字符0
用ASCII码表示是十进制的48
,二进制中是0011 0000
。注意,字符0
和整数0
是不同的。字符用ASCII码表示只占一个字节,十进制为48
,整数0在c语言中占2个字节 ,十进制为0
。
汉字中
已经超出了ASCII码的范围,在Unicode中,十进制表示为20013
,二进制为01001110 00101101
那么字符A
在Unicode中怎么表示呢?
直接在其ASCII码的二进制表示前加0即可,即00000000 01000001
2.4.UTF-8
采用Unicode统一编码的方式,解决了多语言冲突显示乱码的问题,但是
Unicode有一个问题,如果我写的文本基本为英文的话,采用unicode编码的话,该文件所占用的内存空间比采用ASCII编码多一倍,在存储和传输上就极不划算。
由此,人们想出了把Unicode编码转换成“可变长编码”的UTF-8
编码,UTF-8编码把Unicode中的字符按不同的数字大小编码成1-6个字节,英文通常为1个字节,汉字通常为3个字节,只有极其生僻的字符才可能用到4-6个字节。如果要传输的文本以英文为主,则采 用UTF-8编码能节省大量的存储空间。
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 0110 0001 | 00000000 01100001 | 0110 0001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
从上面表格还可以发现,UTF-8已经将ASCII码包含进来了,所以一些历史遗留的只支持ASCII码的软件可以在UTF-8下继续使用。
2.5.国标码(gb)
a. gb码 是中文系统的ANSI编码
b. 由于历史的原因,在Unicode之前,一共存在过3套中文编码标准
中国:
GB2312 1980年 7445个字符 CP936
GBK 1995年 21886个字符 CP936
GB18030 2000年 27484字符 CP54936 不支持手机,MP3
台湾:
Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉字,大概有8千多个。
香港:
HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同。
c. GBK兼容GB2312,加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎不兼容。
3. 应用1
char cone = 1;
char ctwo = 2;
char cthree = 0;
cthree = cone + ctwo;
cout<<"cthree:"<<cthree<<endl;
//输出的是心
char cone1 ='1';
char ctwo1 ='2';
char cthree1 = '3';
cthree1 = cone1 + ctwo1;
cout<<"cthree1:"<<cthree1<<endl;
//输出的是
4. 应用2(表示整数,字符,布尔值的算术类型合称为整型)
trans->type的类型为:unsigned char
5.Unicode Character Set与Multi-Byte Character Set的区别
http://blog.csdn.net/luoweifu/article/details/49382969(带你理解多字节编码与Unicode编码)
5.1 c++基本数据类型中表示整数、字符和布尔的算术类型合称整型。
字符类型有两种:char和wchat_t
char叫多字节字符,一个char占一个字节,之所以叫多字节字符是因为它表示一个字时可能是一个字节也可能是多个字节。一个英文字符(如’s’)用一个char(一个字节)表示,
一个中文汉字(如’中’)用2个char(2个字节)表示,看下面的例子。
void TestChar()
{
char ch1 = 's'; // 正确
cout << "ch1:" << ch1 << endl;
char ch2 = '中'; // 错误,一个char不能完整存放一个汉字信息
cout << "ch2:" << ch2 << endl;
char str[3] = "中"; //前两个字节存放汉字'中',最后一个字节存放字符串结束符\0
cout << "str:" << str << endl;
//char str2[2] = "国"; // 错误:'str2' : array bounds overflow
//cout << str2 << endl;
}
结果如下:
ch1:s
ch2:
str:中
wchar_t被称为宽字符,一个wchar_t占2个字节。之所以叫宽字符是因为所有的字都要用两个字节(即一个wchar_t)来表示,不管是英文还是中文。看下面的例子:
void TestWchar_t()
{
wcout.imbue(locale("chs")); // 将wcout的本地化语言设置为中文
wchar_t wch1 = L's'; // 正确
wcout << "wch1:" << wch1 << endl;
wchar_t wch2 = L'中'; // 正确,一个汉字用一个wchar_t表示
wcout << "wch2:" << wch2 << endl;
wchar_t wstr[2] = L"中"; // 前两个字节(前一个wchar_t)存放汉字'中',最后两个字节(后一个wchar_t)存放字符串结束符\0
wcout << "wstr:" << wstr << endl;
wchar_t wstr2[3] = L"中国";
wcout << "wstr2:" << wstr2 << endl;
}
结果如下:
ch1:s
ch2:中
str:中
str2:中国
说明:
1). 用常量字符给wchar_t变量赋值时,前面要加L。如: wchar_t wch2 = L’中’;
2). 用常量字符串给wchar_t数组赋值时,前面要加L。如: wchar_t wstr2[3] = L”中国”;
3). 如果不加L,对于英文可以正常,但对于非英文(如中文)会出错。
注意:在缺省的C locale下,cout可以直接输出中文,但对于wcout却不行(至少VS 2005下不行)。对于wcout,需要将其locale设为本地语言才能输出中文。(参考自下面的文章)
https://blog.csdn.net/liziyun537/article/details/6024512 wcout的一些介绍
5.2工程里多字节与宽字符的配制
右键你的工程名->Properties,设置如下:
当设置为Use Unicode Character Set时,会有预编译宏:_UNICODE、UNICODE
当设置为Use Multi-Byte Character Set时,会有预编译宏:_MBCS
5.3 Unicode Character Set与Multi-Byte Character Set有什么区别呢?
Unicode Character Set和Multi-Byte Character Set这两个设置有什么区别呢?我们来看一个例子:
有一个程序需要用MessageBox弹出提示框:
#include "windows.h"
void TestMessageBox()
{
::MessageBox(NULL, "这是一个测试程序!", "Title", MB_OK);
}
上面这个Demo非常简单不用多说了吧!我们将Character Set设置为Multi-Byte Character Set时,可以正常编译和运行。但当我们设置为Unicode Character Set,则会有以下编译错误:
error C2664: ‘MessageBoxW’ : cannot convert parameter 2 from ‘const char [18]’ to ‘LPCWSTR’
是因为MessageBox有两个版本,一个MessageBoxW针对Unicode版的,一个是MessageBoxA针对Multi-Byte的,它们通过不同宏进行隔开,预设不同的宏会使用不同的版本。我们使用了Use Unicode Character Set就预设了_UNICODE、UNICODE宏,所以编译时就会使用MessageBoxW,这时我们传入多字节常量字符串肯定会有问题,而应该传入宽符的字符串,即将”Title”改为L”Title”就可以了,”这是一个测试程序!”也一样。
WINUSERAPI int WINAPI MessageBoxA( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); WINUSERAPI int WINAPI MessageBoxW( __in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType); #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif // !UNICOD
6. ANSI,Unicode,utf-8,string之间的转换
6.1.MultiByteToWideChar()
参考:https://docs.microsoft.com/zh-cn/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
调用此函数很容易导致缓冲区溢出,因为lpMultiByteStr指示的输入缓冲区的大小等于字符串中的字节数,而lpWideCharStr指示的输出缓冲区的大小等于字符数。 为避免缓冲区溢出,您的应用程序必须指定适合于缓冲区接收的数据类型的缓冲区大小。
int MultiByteToWideChar
(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cchMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
- 参数:
1> CodePage:指定执行转换的多字节字符所使用的字符集。最常用的应该是CP_ACP和CP_UTF8了,前者将ANSI转换为宽字符,后者将UTF8转换为宽字符。
这个参数可以为系统已安装或有效的任何字符集所给定的值。你也可以指定其为下面的任意一值:
Value | Description |
CP_ACP | ANSI code page 系统默认的Windows ANSI代码页。 |
CP_MACCP | Not supported |
CP_OEMCP | OEM code page |
CP_SYMBOL | Not supported |
CP_THREAD_ACP | Not supported |
CP_UTF7 | UTF-7 code page |
CP_UTF8 | UTF-8 code pag |
2> dwFlags:一组位标记,用以指出是否未转换成预作或宽字符(若组合形式存在),是否使用象形文字替代控制字符,以及如何处理无效字符。
3> lpMultiByteStr:指向待转换的字符串的缓冲区。
4> cchMultiByte:指定由参数lpMultiByteStr指向的字符串中字节的个数(字符串的大小,以字节为单位)。如果字符串以结束符‘\0’结尾,可以设置为-1,会自动判断lpMultiByteStr指定的字符串的长度 (如果字符串不是以空字符中止,设置为-1可能失败,可能成功),此参数设置为0函数将失败。
如果此参数为-1,则该函数将处理整个输入的字符串,包含结束符‘\0’。因此结果Unicode字符串包含一个结束符'\0',并且该函数返回的长度包含该字符。
如果此参数设置为正整数,则该函数将精确处理指定的字节数。如果提供的大小不包含结束符'\0',则所得到的的Unicode字符串不以空字符'\0'结尾,并且返回的长度不包含该字符。
5> lpWideCharStr:指向接收被转换字符串的缓冲区。
6> cchWideChar:指定由参数lpWideCharStr指向的缓冲区的大小(以字符为单位)。若此值为0,函数不会执行转换,而是返回目标缓存lpWideChatStr所需的宽字符数,包含结束符。
- 返回值:如果函数运行成功,并且cchWideChar不为零,返回值是由lpWideCharStr指向的缓冲区中写入的宽字符数;如果函数运行成功,并且cchWideChar为零,返回值是接收到待转换字符串的缓冲区所需求的宽字符数大小。如果函数运行失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。它可以返回下面所列错误代码:
ERROR_INSUFFICIENT_BUFFER;ERROR_INVALID_FLAGS;
ERROR_INVALID_PARAMETER;ERROR_NO_UNICODE_TRANSLATION。
- 注意:
指针lpMultiByteStr和lpWideCharStr必须不一样。如果一样,函数将失败,GetLastError将返回ERROR_INVALID_PARAMETER的值。
如果MB_ERR_INVALID_CHARS被设置并且在资源字符串中遇到无效的字符时,函数将失败。如果MB_ERR_INVALID_CHARS不被设置,或是DBCS串中发现了头字节而没有有效的尾字节,无效字符将转换为缺省字符,但不是资源字符串中的缺省字符。当无效字符被发现,且MB_ERR_INVALID_CHARS值被设置,函数返回零,GetLastErro显示ERROR_NO_UNICODE_TRANSLATION的出错信息。
如果cbhMultiByte为-1,需要注意返回值为包括零字符在内的字符串的总长度比求字符长度函数得到的返回值大1。
Windows CE:不支持参数CodePage中的CP_UTF7和CP_UTF8的值,以及参数dwFlags中的WC_NO_BEST_FIT_CHARS值。
速查:Windows NT 3.1、Windows 95以上、Windows CE 1.0以上,头文件:winnls.h;库文件:kernel32.lib。需要引用的头文件:windows.h。
- 将多字节字符串转为宽字符:
1)获取宽字符所需的缓冲区大小。设置cchWideChar参数为0,调用MultiByteToWideChar()函数,返回值即为宽字符所需缓冲区大小;
2)分配足够内存用于存放转换后的unicode字符
3)再次调用MultiByteToWideChar()函数,这次将缓存的地址作为lpWideCharStr,参数来传递,并传递第一次调用MultiByteToWideChar()函数时的返回值作为cchWideChar参数的值;
4)释放接收缓冲区占用的内存块;
std::wstring ANSI2Unicode(LPCSTR lpszSrc)
{
std::wstring strResult;
if (lpszSrc != NULL)
{
//1.先拿到转换后的宽字符的大小nUnicodeLen
int nUnicodeLen = MultiByteToWideChar(CP_ACP, 0, lpszSrc, -1, NULL, 0);
wchar_t *pUnicode = new wchar_t[nUnicodeLen];
if (pUnicode != NULL)
{
memset(pUnicode, 0, nUnicodeLen * sizeof(wchar_t));
//2.转换
int n = MultiByteToWideChar(CP_ACP, 0, lpszSrc, -1, pUnicode, nUnicodeLen);
strResult = pUnicode;
delete[] pUnicode;
pUnicode = NULL;
}
}
return strResult;
}
6.2.WideCharToMultiByte()
参考:https://docs.microsoft.com/zh-cn/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
int WideCharToMultiByte
(
UINT CodePage,
DWORD dwFlags,
LPWSTR lpWideCharStr,
int cchWideChar,
LPCSTR lpMultiByteStr,
int cchMultiByte,
LPCSTR lpDefaultChar,
PBOOL pfUsedDefaultChar
);
错误使用WideCharToMultiByte函数可能会损害应用程序的安全性。 调用此函数很容易导致缓冲区溢出,因为lpWideCharStr表示的输入缓冲区的大小等于Unicode字符串中的字符数,而lpMultiByteStr表示的输出缓冲区的大小等于字节数。 为避免缓冲区溢出,您的应用程序必须指定适合于缓冲区接收的数据类型的缓冲区大小。
从UTF-16转换为非Unicode编码的数据可能会丢失数据,因为代码页可能无法表示特定Unicode数据中使用的每个字符
- 函数功能:
此函数把宽字符串转换成指定的新的字符串,如ANSI,UTF8等,新字符串不必是多字节字符集。 (---Unicode 转 ANSI(GB2312),UTF8)
- 参数:
1>CodePage:同MultiByteToWideChar
2>dwFlags:指定如何处理没有转换的字符
3>lpWideCharStr: 待转换的宽字符串。
4>cchWideChar: 待转换宽字符串的大小(以字符为单位),-1表示转换到字符串结尾。
如果字符串以结束符‘\0’结尾,则可以将此参数设置为-1。 如果将cchWideChar设置为0,该函数将失败。
如果此参数为-1,则该函数将处理整个输入字符串,包括结束符‘\0’。 因此,结果字符串具有结束符‘\0’,并且函数返回的长度包括该字符。
如果将此参数设置为正整数,则该函数将精确处理指定数量的字符。 如果提供的大小不包含结束符‘\0’,则结果字符串不是以结束符‘\0’终止的,并且返回的长度不包含此字符。
5>lpMultiByteStr: 接收转换后输出新串的缓冲区。
6>cbMultiByte:输出缓冲区大小(以字节为单位),如果为0,lpMultiByteStr将被忽略,函数将返回所需缓冲区大小而不使用lpMultiByteStr。
7>lpDefaultChar: 指向字符的指针, 在指定编码里找不到相应字符时使用此字符作为默认字符代替。如果为NULL则使用系统默认字符。对于要求此参数为NULL的dwFlags而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。
8>lpUsedDefaultChar:开关变量的指针,用以表明是否使用过默认字符。对于要求此参数为NULL的dwFlags而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都设为NULL,函数会更快一些。
- 返回值:如果函数成功,且cbMultiByte非0,返回写入lpMultiByteStr的字节数;cbMultiByte为0,则返回转换所需字节数。函数失败,返回0。
- 注意:函数WideCharToMultiByte使用不当,会给影响程序的安全。调用此函数会很容易导致内存泄漏,因为lpWideCharStr指向的输入缓冲区大小是宽字符数,而lpMultiByteStr指向的输出缓冲区大小是字节数。为了避免内存泄漏,应确保为输出缓冲区指定合适的大小。我的方法是先使cbMultiByte为0调用WideCharToMultiByte一次以获得所需缓冲区大小,为缓冲区分配空间,然后再次调用WideCharToMultiByte填充缓冲区,详见下面的代码。另外,从Unicode UTF16向非Unicode字符集转换可能会导致数据丢失,因为该字符集可能无法找到表示特定Unicode数据的字符。
- 将宽字符转为多字节字符串:
std::string Unicode2ANSI(LPCWSTRlpszSrc)
{
std::string strResult;
if (lpszSrc != NULL)
{
int nANSILen = WideCharToMultiByte(CP_ACP, 0, lpszSrc, -1, NULL, 0, NULL, NULL);
char * pANSI = new char[nANSILen];
if (pANSI != NULL)
{
memset(pANSI, 0, nANSILen);
WideCharToMultiByte(CP_ACP, 0, lpszSrc, -1, pANSI, nANSILen, NULL, NULL);
strResult = pANSI;
delete[] pANSI;
pANSI = NULL;
}
}
return strResult;
}
6.3.utf8和unicode互转
- Unicode2UTF8
std::string Unicode2UTF8(LPCWSTR lpszSrc)
{
std::string strResult;
if (lpszSrc != NULL)
{
int nUTF8Len = WideCharToMultiByte(CP_UTF8, 0, lpszSrc, -1, NULL, 0, NULL, NULL);
char * pUTF8 = new char[nUTF8Len];
if (pUTF8 != NULL)
{
memset(pUTF8, 0, nUTF8Len);
WideCharToMultiByte(CP_UTF8, 0, lpszSrc, -1, pUTF8, nUTF8Len, NULL, NULL);
strResult = pUTF8;
delete[] pUTF8;
pUTF8 = NULL;
}
}
return strResult;
}
- UTF82Unicode
std::wstring UTF82Unicode(const std::string &utf8_str)
{
std::wstring strResult;
int nUnicodeLen = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, NULL, 0);
wchar_t *pUnicode = new wchar_t[nUnicodeLen];
if (pUnicode != NULL)
{
memset(pUnicode, 0, nUnicodeLen * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, pUnicode, nUnicodeLen);
strResult = pUnicode;
delete[] pUnicode;
pUnicode = NULL;
}
return strResult;
}
6.4.sring(ANSI)和utf8互转
string转utf8:string->unicode, unicode->utf8
utf8转string:utf8->unicode, unicode->string
- string2UTF8
std::string string2UTF8(const std::string & str)
{
std::string strResult;
//1.string 转unicode
int nUnicodeLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t *pUnicode = new wchar_t[nUnicodeLen];
if (pUnicode != NULL)
{
memset(pUnicode, 0, nUnicodeLen * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, nUnicodeLen);
//2.unicode转utf8
int nUTF8Len = WideCharToMultiByte(CP_UTF8, 0, pUnicode, -1, NULL, 0, NULL, NULL);
char *pUTF8 = new char[nUTF8Len];
if (pUTF8 != NULL)
{
memset(pUTF8, 0, nUTF8Len);
WideCharToMultiByte(CP_UTF8, 0, pUnicode, -1, pUTF8, nUTF8Len, NULL, NULL);
strResult = pUTF8;
delete[] pUTF8;
pUTF8 = NULL;
}
delete[] pUnicode;
pUnicode = NULL;
}
return strResult;
}
- UTF82string
std::string UTF82string(const std::string & str)
{
std::string strResult;
//1.utf8转unicode
int nUnicodeLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t *pUnicode = new wchar_t[nUnicodeLen];
if (pUnicode != NULL)
{
memset(pUnicode, 0, nUnicodeLen * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, pUnicode, nUnicodeLen);
//2.unicode转string
int nLen = WideCharToMultiByte(CP_ACP, 0, pUnicode, -1, NULL, 0, NULL, NULL);
char *pANSI = new char[nLen];
if (pANSI != NULL)
{
memset(pANSI, 0, nLen);
WideCharToMultiByte(CP_ACP, 0, pUnicode, -1, pANSI, nLen, NULL, NULL);
strResult = pANSI;
delete[] pANSI;
pANSI = NULL;
}
delete[] pUnicode;
pUnicode = NULL;
}
return strResult;
}
参考文章:
1.百度百科ASCII 点击打开链接
2.字符集和字符编码(Charset & Encoding)http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html
3.带你理解多字节编码与Unicode编码 http://blog.csdn.net/luoweifu/article/details/49382969
4.几种字符编码 https://www.jianshu.com/p/ed948590a3af
5.字符编码ANSI和ASCII区别、Unicode和UTF-8区别 https://www.cnblogs.com/dedema/p/10851740.html
6.字符编码ASCII、ANSI、Unicode、UTF-8、GB2312 https://www.jianshu.com/p/1f36bc2a247f