字符编码:ANSI、ASCII、Unicode、UTF-8、UTF-16、UTF-32概念和格式转换

106 篇文章 3 订阅
76 篇文章 0 订阅

1、ASCII

ASCII码是字符集,使用指定的7 位或8 位二进制数组合来表示128或256 种可能的字符。分为标准ASCII 、扩展ASCII 。

标准ASCII 码也叫基础ASCII码,使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。其中:

0~31及127(共33个,0×20以下)是控制字符或通信专用字符(其余为可显示字符);

32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字。

其中65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。扩展ASCII主要是加入了新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。

参考:https://blog.csdn.net/u012152619/article/details/43235747

ASCII对照表:http://ascii.911cha.com/

2、ANSI

ANSI又可以称为多字节字符集,0-127(0x00~0x7F)字符与标准ASCII 一致,依旧是1个字节代表1个字符,所以ANSI又可以简单的认为是对标准ASCII的扩展,因为它包括了标准ASCII。在127之后采用多个字节存储文字,通常最为常见的是双字节字符集(DBCS,Double-Byte CharacterSet)。

ASCII是美国标准,所以它不能良好满足其它国家的需要。例如英国的英镑符号(£),拉丁语字母表重音符号,使用斯拉夫字母表的希腊语、希伯来语、阿拉伯语和俄语,还有汉字系统的中国象形汉字,日本语和朝鲜语等等。

多字节字符集 (MBCS,Multi-ByteChactacter Set) 是一种旧的方式以支持无法用单字节表示的字符集(如日文和中文)的方法。最常见的 MBCS 实现是双字节字符集 (DBCS,Double-Byte CharacterSet)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

各个国家为了扩充ASCII编码,以用于显示本国的语言,不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准,可以说ANSI编码在各个国家的编码方式都不同,所以造成了的国家间的交流问题,所以这也是促使了Unicode的出现。

在简体中文系统下,ANSI编码代表 GB2312 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文操作系统下,ANSI 编码代表 JIS 编码。所以在中文 windows下要转码成gb2312,gbk只需要把文本保存为ANSI 编码即可。

参考文章:https://blog.csdn.net/u012152619/article/details/43235747

3、Unicode

如上ANSI编码条例中所述,世界上存在着多种编码方式,在ANSI编码下,同一个编码值,在不同的编码体系里代表着不同的字。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码,可能最终显示的是中文,也可能显示的是日文。在ANSI编码体系下,要想打开一个文本文件,不但要知道它的编码方式,还要安装有对应编码表,否则就可能无法读取或出现乱码。为什么电子邮件和网页都经常会出现乱码,就是因为信息的提供者可能是日文的ANSI编码体系和信息的读取者可能是中文的编码体系,他们对同一个二进制编码值进行显示,采用了不同的编码,导致乱码。这个问题促使了unicode码的诞生。

如果有一种编码,将世界上所有的符号都纳入其中,无论是英文、日文、还是中文等,大家都使用这个编码表,就不会出现编码不匹配现象。每个符号对应一个唯一的编码,乱码问题就不存在了。这就是Unicode编码。

参考文章:https://blog.csdn.net/u012152619/article/details/43235747

Unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,Unicode包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。

Unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!同时,也都是统一的”两个字节“,请注意”字符”和”字节”两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在Unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。

Unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别UnicodeASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。

这就导致Unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种Unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从Unicode到utf-8并不是直接的对应,而是要过一些算法和规则来转换。

参考文章:https://www.zhihu.com/question/23374078/answer/69732605

4、UTF-8、UTF-16、UTF-32

容易混淆的概念:https://blog.csdn.net/u011447369/article/details/55504678

区别编码字符集和字符集编码

首先要注意的是ASCII、ANSI、Unicode是编码字符集,而UTF-8、UTF-16、UTF-32是字符集编码(好绕哎有没有)。下面我来具体解释一下:

比如汉字的”汉”,在Unicode中,汉”的Unicode值为0x6C49。问:把这个”汉”字保存到计算机中(硬盘、内存),机器码是多少呢? 

学过《计算机组成原理》的人都知道,计算机内部存储的形式都是0101的二进制数字串。”汉”字保存在计算机里肯定也是0101的数字串。”汉”的Unicode值是0x6C49,转化为2进制 1101100 01001001,那么把这个”汉”字保存到计算机中也是 1101100 01001001 吗?答案:NO!

答案:取决于用到的字符集编码是哪种

比如你用到的字符集编码是UTF-8,那么”汉”字在计算机内部保存的值为0xE6B189,也就是111001101011000110001001,可以看到”汉”字变成了3个字节。UTF-8用1-3个字节来保存unicode编码的字符。 

而如果用UTF-16来保存,那么”汉”字仍为仍为0x6C49,也就是 1101100 01001001。UTF-16只能是选两字节或四字节来保存字符,通常是两字节表示,而Unicode编码字符集一般刚好也是用两字节表示一个字符,所以两者虽然概念上不等同,但是实际上是一样的。

而UTF-32就是把所有的字符都用32bit也就是4个字节来表示。 

所以这就是编码字符集和字符集编码的区别。

参考文章:https://blog.csdn.net/co_yiqiu/article/details/54954392

文本文档是用UTF-8编码保存的。这样可以最大限度的节省空间。但是当你对这个文本文档进行操作的时候,计算机是先把UTF-8转化为Unicode然后放到内存中,让用户进行操作,操作完成后在内存中还是Unicode模式。当你需要保存的时候在转化为UTF-8格式保存(节省空间)。

参考文章:https://blog.csdn.net/dongchongyang/article/details/52484794

总结:以文本形式保存的文件(.txt,.h,.cpp等)存储方式,可以分为ANSI/ASCll(即多字节编码)、带BOM的UTF-8、不带BOM的UTF-8、带BOM的UTF-16、不带BOM的UTF-16等。

BOM概念看:https://blog.csdn.net/u014563989/article/details/53087470

对于以UTF-8存储的文件,在文件头会有三个隐藏的字节0xEF、0xBB、0xBF(直接以文本形式打开无法看到,只有在16进制下看才可以看到,去掉这三个字节方法参考:https://blog.csdn.net/wuzoujing/article/details/51860830

FE FF  UTF-16 (big-endian)
FF FE  UTF-16 (little-endian)
00 00 FE FF  UTF-32 (big-endian)
FF FE 00 00  UTF-32 (little-endian)

编码发展历史:https://www.zhihu.com/question/23374078/answer/69732605

5、ANSI、UTF-8、UTF-16、Unicode三者之间相互转换

(1)UTF-8转Unicode

//UTF-8转Unicode
std::wstring Utf82Unicode(conststd::string& utf8string)
{
intwidesize =::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, NULL, 0);
if (widesize ==ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8sequence.");
}
if (widesize == 0)
{
throw std::exception("Error inconversion.");
}
std::vector<wchar_t>resultstring(widesize);
intconvresult =::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, &resultstring[0],widesize);
if (convresult != widesize)
{
throw std::exception("Lafalla!");
}
returnstd::wstring(&resultstring[0]);
}

不用vector<wchar_t>也可以实现

inline BOOL CFileCountDlg::UTF8ToWide(std::string &UTF8Str, std::wstring &UnicodeStr)
{
	int minSize = MultiByteToWideChar(CP_UTF8, 0, UTF8Str.c_str(), -1, NULL, 0);
	wchar_t *wchar = new wchar_t[minSize];
	MultiByteToWideChar(CP_UTF8, 0, UTF8Str.c_str(), -1, wchar, minSize);
	UnicodeStr = wchar;
	delete[] wchar;

	return TRUE;
}

(2)Unicode转ANSI

//unicode转为 ascii
string WideByte2Acsi(wstring& wstrcode)
{
intasciisize =::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, NULL, 0, NULL, NULL);
if (asciisize ==ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8sequence.");
}
if (asciisize == 0)
{
throw std::exception("Error inconversion.");
}
std::vector<char>resultstring(asciisize);
intconvresult=::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, &resultstring[0],asciisize, NULL, NULL);
if (convresult != asciisize)
{
throw std::exception("Lafalla!");
}
returnstd::string(&resultstring[0]);
}

不用vector<wchar_t>也可以实现

注意:CP_OEMCP也可以用CP_ACP参数

inline BOOL CFileCountDlg::WideToANSI(std::wstring &UnicodeStr, CString &ANSIStr)
{
	int minSize = WideCharToMultiByte(CP_OEMCP, 0, UnicodeStr.c_str(), -1, NULL, 0, NULL, NULL);
	char *wchar = new char[minSize];
	WideCharToMultiByte(CP_OEMCP, 0, UnicodeStr.c_str(), -1, wchar, minSize, NULL, NULL);
	ANSIStr = wchar;
	delete[] wchar;
	return TRUE;
}

(3)UTF-8转ANSI

需要借助Unicode为桥梁,转换

//utf-8转 ascii
string UTF_82ASCII(string& strUtf8Code)
{
string strRet("");
//先把 utf8 转为 unicode
wstring wstr = Utf82Unicode(strUtf8Code);
//最后把 unicode 转为 ascii
strRet = WideByte2Acsi(wstr);
returnstrRet;
}

(4)ANSI转Unicode

//ascii转 Unicode
wstring Acsi2WideByte(string& strascii)
{
intwidesize =MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1, NULL, 0);
if (widesize ==ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8sequence.");
}
if (widesize == 0)
{
throw std::exception("Error inconversion.");
}
std::vector<wchar_t>resultstring(widesize);
intconvresult =MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1,&resultstring[0], widesize);
if (convresult != widesize)
{
throw std::exception("Lafalla!");
}
returnstd::wstring(&resultstring[0]);
}

(5)Unicode转UTF-8

//Unicode转 Utf8
std::string Unicode2Utf8(conststd::wstring& widestring)
{
intutf8size =::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, NULL, 0, NULL, NULL);
if (utf8size == 0)
{
throw std::exception("Error inconversion.");
}
std::vector<char>resultstring(utf8size);
intconvresult =::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, &resultstring[0],utf8size, NULL, NULL);
if (convresult != utf8size)
{
throw std::exception("Lafalla!");
}
returnstd::string(&resultstring[0]);
}

(6)ANSI转UTF-8

//ascii转 Utf8
string ASCII2UTF_8(string&strAsciiCode)
{
string strRet("");
//先把 ascii 转为 unicode
wstring wstr = Acsi2WideByte(strAsciiCode);
//最后把 unicode 转为 utf8
strRet = Unicode2Utf8(wstr);
returnstrRet;
}

以上(1)-(6)转换参考文章:https://blog.csdn.net/jqsad/article/details/51661658

(7)ANSIUTF-16

// ansi在本机是MBCS(实际上是DBCX) 转为UNIcode默认的UTF16编码
std::wstring ANSI_2_UTF16( const string& strANSI )
{
	int nUnicodeLength = ::MultiByteToWideChar(CP_ACP,0,strANSI.c_str(),-1,NULL,0)	;

	wstring strUTF16(nUnicodeLength,_T(' '));
	int nRet = ::MultiByteToWideChar(CP_ACP,0,strANSI.c_str(),-1,&strUTF16[0],nUnicodeLength);
	ASSERT(0 != nRet);
	
	return strUTF16;
}

(8)UTF-16转ANSI(UTF-16可以认为是Unicode,与上面Unicode转ANSI方法一摸一样)

// UNIcode默认的UTF16编码转为 ansi 在本机是MBCS(实际上是DBCX) 
std::string UTF16_2_ANSI( const wstring& strUTF16 )
{
	int nANSILength = ::WideCharToMultiByte(CP_ACP,0,strUTF16.c_str(),-1,NULL,0,0,0);

	string strANSI(nANSILength,' ');
	int nRet = ::WideCharToMultiByte(CP_ACP,0,strUTF16.c_str(),-1,&strANSI[0],nANSILength,0,0);
	ASSERT(0 != nRet);
	return strANSI;
}

(9)UTF-16转UTF-8

// UNIcode默认的UTF16编码转为 UTF8编码
std::string UTF16_2_UTF8( const wstring& strUTF16 )
{
	int nUTF8Length = ::WideCharToMultiByte(CP_UTF8,
		0,
		strUTF16.c_str(),
		-1,
		NULL,
		0,
		0,0);
	
	string strUTF8(nUTF8Length+1,'\0');
	int nRet = ::WideCharToMultiByte(CP_UTF8,
		0,
		strUTF16.c_str(),
		-1,
		&strUTF8[0],
		nUTF8Length+1,
		0,
		0);


	return strUTF8;
}

(10)UTF-8转UTF-16

// UNIcode UTF8编码 转为 UNIcode默认的UTF16编码
std::wstring UTF8_2_UTF16( const string& strUTF8 )
{
	int nUTF16Length = ::MultiByteToWideChar(CP_UTF8,0,strUTF8.c_str(),-1,NULL,0);

	nUTF16Length += 1;
	wstring strUTF16(nUTF16Length ,' ');

	int nRet = ::MultiByteToWideChar(CP_UTF8,0,strUTF8.c_str(),-1,
		&strUTF16[0],nUTF16Length);
	ASSERT(0 != nRet);

	return strUTF16;
}

以上(7)-(10)转换参考:https://blog.csdn.net/zy_dreamer/article/details/8871738(ANSI UTF16 UTF8转换,也有ANSI与UTF-8间的转换,不过也是无法直接转换,借助了UTF-16完成了转换)

(11)三者文件处理中注意事项

上边6中转换仅仅是字符串的转换,以UTF形式存储的文本文件一般都有BOM标志(也可以没有,当时一般都会有,而且建议在输出UTF文件时都输出UTF的BOM标志),所以在读取UTF文本文件时,可以根据BOM标志判断是UTF-8、UTF-16还是ANSI,如果是UTF文件,应该把BOM标志跳过,不读取。

(12)两个主要函数参数说明

MultiByteToWideChar、WideCharToMultiByte函数参数

参考:https://blog.csdn.net/ccfxue/article/details/51007824

(13)用C++11函数实现

上述整套方法是用Windows API函数实现的,在C++11中有封装好的函数实现格式转换

参考1:https://blog.csdn.net/ccfxue/article/details/51007824

参考2:http://www.cppblog.com/Error/archive/2014/09/25/208413.html

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值