字符集和编码

字符 Character: 文字符号
字符集 Charset:字符的集合,规定一系列的字符与数字的对应关系(编码)
字符集都是由一些组织制定和发布的,例如ISO/IEC就负责制定字符集
每个字符集都有一个标准编号,例如:ASCII字符集的编号为ISO/IEC 646(即由ISO/IEC发布的第646号标准文档)

拉丁字符集(Latin)

拉丁字符集收录了欧洲各个国家的字符,例如希腊字母 Aα Bβ
拉丁字符集的编号为ISO 8859系列
例如:
ISO8859-1字符集,也就是Latin-1,是西欧常用字符,包括德法两国的字母。
ISO8859-2字符集,也称为Latin-2,收集了东欧字符。

由于一个字节只能表示最多127个字符(0x00-0xFF),那么要表示其他的字符就必须使用2个字节或者更多才行
ASCII码:0-127之间,最高位都为0
拉丁字符或其他字符:最高位都为1

中文字符集

GB2312 中文简体国际码(汉字数:6763)
GBK 扩展中文GB编码(兼容GB2312)
GB18030 用1-4个字节编码,容量超大
BIG5 中文繁体编码
CJK 中日韩大字符集编码
Unicode 统一码,用0-0x10FFFF来映射全球各国的语言文字

观察GBK编码
下面我们用VsCode的hexdump插件观察文本的编码
注意:确定了文件的字符集编码之后,才能知道究竟存储了那些字节。
如下图,“我们”两字对应的GBK编码:
在这里插入图片描述
在这里插入图片描述

结论GBK编码:中文占用两个字节,英文和半角标点(0-127)占用一个字节

细说Unicode

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案
Unicode用数字0-0x10FFFF来映射所有字符,最多可以容纳1114112个字符。
可见Unicode字符集是一个超大字符集
从他的范围(0-0x10FFFF)可以看出Unicode至少需要3个字节才能容纳这么多字符
那么问题来了?用几个字节来表示一个字符呢?

第一种方案:UTF32
每个字符用一个int来表示
特点:简单,但是浪费空间,比如我要表示”a“这个字母,难道用4个字节来表示?那也太浪费了。

第二种方案:UTF16
用1~2个short表示一个字符,比如“a”用1个short即两个字节就可以表示,而“我”用2个short即四个字节来表示,这句话可能比较难理解,继续往下看UTF8编码方案你就明白了。

第三种方案:UTF8
用1~4个字节来表示一个字符(比较节省空间)

首先我们知道Unicode的编码范围为:0-0x10FFFF, 每一个字符都对应了一个范围内的一个数,
如果一个字符对应的数比较小他在000000-00007F之间,那么就用一个字节来表示0xxxxxxx
如果一个字符对应的数在000080-0007FF之间,那么就用两个字节来表示110xxxxx 10xxxxxx
以此类推,如下表格:

Unicode编码(十六进制)UTF-8字节流(二进制)
000000-00007F0xxxxxxx
000080-0007FF110xxxxx 10xxxxxx
000800-007FFF1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

不同的区间用不同的字节来表示,做少1个字节最多4个字节,那如果我们用纯英文的话,那就是一个字符占用一个字节,如果使用汉字的话就不一定的,要看这个汉字坐落在那个区间,有可能是两个2字节,也有可能是3个字节

下面我们在回过头看一下UTF16编码,我们使用十六进制来观察下UTF16编码:
在这里插入图片描述
hexdump导出的内容前面的FE FF标识该文件的编码方式,可以忽略。
从图上我们可以看出,一个字母占用了2个字节,这就和utf-16的编码方案有关了(用1~2个short表示一个字符)。
我们再使用UTF8编码来观察一下:
在这里插入图片描述
是不是发现一个字母只占用一个字节了。

这里要特别提一下,”我们“这两个字在UTF16中占用2个字节,而在UTF8中占用3个字节,所以使用UTF8不一定就节省空间,使用UTF16也不一定浪费空间。

补充-在c/c++中的问题

“我们”
在UTF16编码中为:62 11 4E EC
在GBK编码中为:CE D2 C3 C7

用一个数字来保存人名:

// vs中使用的GBK编码
char name[] = {0xCE, 0xD2, 0xC3, 0xC7};

所以你必须指明你使用的字符集/编码是哪一种,不然别人是不知道你的内容是什么的

编码转换

假如你的字符串按照GBK编码但对方要求按UTF16编码

// vc环境,这里strlen实际长度为5:gbk编码一个汉字使用2个字节,加上结束符\0
char str[] = "我们";

四个字节发送给对方,但是对方只接受UTF16编码,这时我们就要把GBK编码转换成UTF16

VC下的转换方法

在vc中,用wchar_t代表宽字符,相当于short型。
我们做一下的转换:

GBK->UTF16

GBK->UTF16->UTF8

在代码中写出来的“我们”两字,它究竟对应了那几个字节?这取决于CPP文件本身的编码。
比如你在Centos Linux上,默认的字符集是UTF-8的,所以它是6个字节的。
而在Windows中文版上,默认按GBK存储,所以它是4字节的。

下面将gbk转成UTF16

#include <stdio.h>
#include <winsock2.h>
#include<Windows.h>

// gbk->utf16
int gbk_to_utf16() {
	// 可以在调试窗口观察
	// 在GBK编码下相当于 char text_gbk[] = {0xCE, 0xD2, 0xC3, 0xC7, 0};
	char text_gbk[] = "我们"; // 字符串字面常量,实际字节取决于文件本身的字符编码
	wchar_t text_utf16[256];
	//CP_ACP 使用系统默认的代码页,中文系统是GBK
	int n = MultiByteToWideChar(CP_ACP, 0, text_gbk, strlen(text_gbk), text_utf16, 256);

	printf("%d个宽字符\n", n);
	return 0;
}

MultiByteToWideChar函数说明 : 将字符串映射为UTF-16(宽字符)字符串
上面的输出结果会输出2个宽字符,即”我们“这两个字在UTF16里每个字占用一个unsigned short型,也就是两个字节, 共4个字节。

下面这个例子将gbk转成utf16,在把utf16转成utf8

#include <stdio.h>
#include <winsock2.h>
#include<Windows.h>
// gbk->utf16->utf8
int gbk_to_utf16_to_utf8() {
	// gbk->utf16
	char text_gbk[] = "我们"; // 字符串字面常量,实际字节取决于文件本身的字符编码
	wchar_t text_utf16[256];
	int n1 = MultiByteToWideChar(CP_ACP, 0, text_gbk, strlen(text_gbk), text_utf16, 256);
	printf("%d个宽字符\n", n1);

	// 转成utf-8后,我们这两个字在UTF8里占用6个字节
	char text_utf8[256];
	int n2 = WideCharToMultiByte(CP_UTF8,0,text_utf16,n1,text_utf8,256,NULL,0);
	printf("%d个字节\n", n2);
	return 0;
}

MultiByteToWideChar函数说明:将UTF-16(宽字符)字符串映射为新字符串
上面例子把utf16编码的“我们”转成UTF8之后每个字占用了三个字节,一共占用6个字节。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值