字节、字符和编码

本文详细介绍了字符编码的原因,从ASCII码、ISO-8859标准到GBK和Unicode字符编码,以及UTF-8和UTF-16的实现。特别提到了Java中字符编码使用UTF-16BE,并通过示例展示了字节与字符之间的转换。

在这里插入图片描述
字节(Byte):一个字节占8位(bit),在计算机和网络中是通过二进制字节传递信息的;

字符(character):字符是人所能读写的最小单位,例如汉字“我”、字母“A”、符合“+”等,计算机所能支持的字符组成的集合,就叫做字符集。

编码(encode):在计算机和网络中无法直接传递字符,所以需要将字符解析为字节,个解析操作就叫做编码(encode),而相应的,将编码的字节还原成字符的操作就叫做解码(decode)。编码和解码都需要按照一定的规则,这种把字符集中的字符编码为特定的二进制数的规则就是字符编码(Character encoding)。

编码的原因

由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元——字节 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。计算机中存储信息的最小单元是一个字节即8个位(bit),所以能表示的字符范围是0~255个。人类要表示的符号太多,无法用一个字节来完全表示,这就需要编码来解决这个问题。

ASCII码

在计算机发展的早期,字符集和字符编码一般使用相同的命名,例如最早的字符集ASCII(American Standard Code for Information Interchange),它既代表了计算机所支持显示的所有字符(字符集),又代表了这个字符集的字符编码。

ASCII字符集是一个二维表,支持128个字符。128个码位,用7位二进制数表示,由于计算机1个字节是8位二进制数,所以最高位为0,即00000000-01111111 或0x00-0x7F。

慢慢地,只支持128个码位的ASCII不能满足我们的需求了,就在原来ASCII的基础上,扩展为256个字符,成为EASCII(Extended ASCII)。EASCII有256个码位,用8位二进制数表示,即00000000-11111111或0x00-0xFF。

ISO-8859标准

当计算机传到了欧洲,EASCII也开始不能满足需求了,但是改改还能凑合。于是国际标准化组织在ASCII的基础上进行了扩展,形成了ISO-8859标准,跟EASCII类似,兼容ASCII,在高128个码位上有所区别。但是由于欧洲的语言环境十分复杂,所以根据各地区的语言又形成了很多子标准,如ISO-8859-1、ISO-8859-2、ISO-8859-3、……、ISO-8859-16。

所以ISO-8859并不是一个标准,而是一系列标准,它们子标准都以同样的码位对应不同字符集。

GBK

全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。

Unicode字符编码

后来计算机传入亚洲,由于亚洲语种和文字都十分丰富,256个码位就显得十分鸡肋了。于是继续扩大二维表,单字节改双字节,16位二进制数,65536个码位。在不同国家和地区又出现了很多编码,大陆的GB2312、港台的BIG5、日本的Shift JIS等等。

为了解决传统的字符编码方案的局限,就引入了统一码(Unicode)和通用字符集(Universal Character Set, UCS)来替代原先基于语言的系统。通用字符集的目的是为了能够涵盖世界上所有的字符。Unicode是计算机科学领域里的一项业界标准,包括字符集、编码方案等。

Unicode的编码方式:Unicode使用16位的编码空间也就是每个字符占用2个字节。这样理论上一共最多可以表示2的16次方(即65536)个字符。

Unicode的实现方式:Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式也称为Unicode转换格式(Unicode Transformation Format,简称为UTF),目前主流的实现方式有UTF-16和UTF-8。

UTF-8

UTF-8是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换。
UTF-8每个字符使用1-3个字节编码,并利用首位为0或1进行识别。

1、如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
2、如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
3、如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节

UTF-16

UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

Java中的字符编码

在Java中,char类型用UTF-16BE编码描述一个代码单元。在Java SE 1.4中引入的java.nio包用Charset类统一了对字符集的转换。此类定义了用于创建解码器和编码器以及获取与 charset 关联的各种名称的方法。此类的实例是不可变的。

下面用一个小例子来展示一下Java中字节与字符之间的转换:

package com.test;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

public class Test {

public static void main(String[] args) {
	// TODO Auto-generated method stub
	String s = "Hello World,你好 世界";
	System.out.println(s);
	
	byte[] byte1 = s.getBytes();//默认编码
	System.out.println("默认编码:" + Charset.defaultCharset());
	for(byte b : byte1) {
		//把字节以16进制显示
		System.out.print(Integer.toHexString(b & 0xff) + " ");
	}
	
	try {
		byte[] byte2 = s.getBytes("UTF-8");
		System.out.println("\nUTF-8编码:");
		for(byte b : byte2) {
			//把字节以16进制显示
			System.out.print(Integer.toHexString(b & 0xff) + " ");
		}
	} catch (UnsupportedEncodingException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	try {
		byte[] byte3 = s.getBytes("UTF-16");
		System.out.println("\nUTF-16编码:");
		for(byte b : byte3) {
			//把字节以16进制显示,fe ff表示是以UTF-16编码
			System.out.print(Integer.toHexString(b & 0xff) + " ");
		}
	} catch (UnsupportedEncodingException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
}

}

在这里插入图片描述

### 字符编码字节的关系 字符编码是一种将字符标准化并将其映射为计算机能够理解的二进制形式的方法。这种映射过程通过定义一系列规则实现,使得每一个字符都能被唯一地表示为一组特定长度或可变长度的字节序列。 #### 单字节字符编码字节字符编码是最简单的字符编码方式之一,它使用一个字节(即8位)来表示一个字符。由于一个字节能表示的最大数值范围是从0到255,因此这类编码通常用于表示ASCII字符集中的128个基本字符以及扩展字符集中额外的128个字符[^2]。 #### 双字节字符编码字节字符编码则需要两个字节来表示一个字符。这种方式主要用于支持更多的字符集合,尤其是像中文、日文韩文这样的复杂文字系统。例如,在GBK或GB2312编码中,汉字通常是通过两个连续的字节来进行表示的。 #### 多字节字符编码字节字符编码采用的是更为灵活的方式——可以使用不同数量的字节来代表不同的字符。这允许更高效地利用空间资源,并能覆盖全球几乎所有的书面语言符号。典型的例子包括Unicode标准下的UTF-8编码方案。在UTF-8中,英文字母可能只需要一个字节即可表达;而对于其他较为复杂的字符,则可能会占用两至四个甚至更多字节不等。 #### 宽字节 vs 窄字节 当提到宽字节字节时,我们讨论的是每个字符所占内存单元大小的区别。“窄字节”一般指代那些仅需一到几个固定字节数量就能完成全部字符描述的情况;相对应,“宽字节”意味着更大的基础单位尺寸,比如C++里的wchar_t类型,默认情况下可能是两个或者四个字节宽度用来储存单一字符数据[^3]。 需要注意的是,虽然某些多字节编码也可能基于较宽带度构建而成,但这并不意味着所有多字节都是宽字节,反之亦然。正如前面提及过的UTF-8实例那样,尽管它是个多字节体系结构,但由于其内部操作是以一个个独立的一字节块作为最小处理颗粒度执行下去的缘故,所以我们仍然把它分类归属于“窄字节”的范畴之内。 综上所述,字符编码决定了如何把抽象意义上的字符转换成为实际存在于硬件层面之上的具体比特流表现形态,而这个过程中涉及到的具体细节就包含了上述各种类型的区分及其相互间错综复杂却又井然有序联系网络之中去了。 ```python # Python示例展示字符串在不同编码下对应的字节数组 s_ascii = 'A' print(s_ascii.encode('ascii')) # 输出 b'A' s_utf8 = '你好' print(s_utf8.encode('utf-8')) # 输出 b'\xe4\xbd\xa0\xe5\xa5\xbd' s_gbk = '你好' print(s_gbk.encode('gbk')) # 输出 b'\xc4\xe3\xba\xc3' ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值