Golang:unicode与字符编码

字符编码基础

  • Go语言中标识符可以包含“任何Unicode编码可以表示的字母字符”。
  • 当将整数转换为string类型时,如果整数值不能代表一个有效的Unicode代码点,转换的结果就会是"�"。
  • 当一个string类型的值被转换为[]rune类型时,其中的字符串会被拆分成一个一个的Unicode字符。
  • Go语言采用的字符编码方案从属于Unicode编码规范,所有源代码,都必须按照Unicode编码规范中的UTF-8编码格式进行编码

ASCII编码

  • ASCII是American Standard Code for Information Interchange的缩写,中文翻译为美国信息交换标准代码,它是由美国国家标准学会(ANSI)制定的单字节字符编码方案,可用于基于文本的数据交换。最初是美国的国家标准,后又被国际标准化组织(ISO)定为国际标准,称为ISO 646标准,并适用于所有的拉丁文字字母。
  • ASCII编码方案使用单个字节(byte)的二进制数来编码一个字符。标准的ASCII编码用一个字节的最高比特(bit)位作为奇偶校验位,而扩展的ASCII编码将此位也用于表示字符。
  • ASCII编码支持的可打印字符和控制字符的集合,也被叫做ASCII编码集。

Unicode编码规范

  • Unicode编码规范是另一个更通用的,针对书面字符和文本字符的编码标准,为世界上现存的所有自然语言中的每一个字符,都设定了一个唯一的二进制编码。
  • 它定义了不同自然语言的文本数据在国际间交换的统一方式,并为全球化软件创建了一个重要的基础。Unicode编码规范以ASCII编码集为出发点,并突破了ASCII只能对拉丁字母进行编码的限制
  • 在计算机系统内部,抽象的字符会被编码为整数,这些整数的范围被称为代码点,在代码空间之内,每一个特定的整数都被称为一个代码点。
  • Unicode编码规范通常使用十六进制来表示Unicode代码点的整数值,并使用U+作为前缀,比如,英文字母"a"的Unicode代码点是U+0061
  • Unicode编码规范提供了三种编码格式:UTF-8、UTF-16、UTF-32,UTF是UCS Transformation Format的缩写,UCS又是Universal(或Unicode)Character Set的缩写,所以UTF也可以被翻译为Unicode转换格式。它代表的是字符与字节序列之间的转换方式。末尾的数字表示以多少个比特位作为一个编码单元。比如UTF-8表示以8比特作为一个编码单元,并且它和标准的ASCII编码完全兼容,也就是说在[0x00,0x7F]范围内,这两种编码表示的字符都是相同的,这也是UTF-8编码格式的巨大优势之一
  • UTF-8是可变宽的编码方案,它会用一个或多个字节的二进制数表示某个字符,最多四个字节。比如一个英文字符只需要一个字节,中文字符需要3个字节。

string类型的值在底层如何表达?

  • 在底层一个string类型的值是由一系列相对应的Unicode代码点的UTF-8编码值来表达的。
  • 在Go语言中,string类型既可以拆分成一个包含多个字符的序列([]rune),也可以被拆分成包含多个字节的序列([]byte)
  • rune(int32的别名类型)的一个值就代表一个Unicode字符。一个rune类型的值在底层其实就是一个 UTF-8 编码值。前者是(便于我们人类理解的)外部展现,后者是(便于计算机系统理解的)内在表达。
	str := "Go爱好者"
	fmt.Printf("The string: %q\n", str)
	fmt.Printf("  => runes(char): %q\n", []rune(str))
	fmt.Printf("  => runes(hex): %x\n", []rune(str))
	fmt.Printf("  => bytes(hex): [% x]\n", []byte(str))
	fmt.Println(len(str))

	for i, c := range str {
		fmt.Printf("%d: %q [% x]\n", i, c, []byte(string(c)))
	}


	The string: "Go爱好者"
	  => runes(char): ['G' 'o' '爱' '好' '者']
	  => runes(hex): [47 6f 7231 597d 8005]
	  => bytes(hex): [47 6f e7 88 b1 e5 a5 bd e8 80 85]
	11
	0: 'G' [47]
	1: 'o' [6f]
	2: '爱' [e7 88 b1]
	5: '好' [e5 a5 bd]
	8: '者' [e8 80 85]
  • 从输出可以看出一个汉字3个字节,同时汉字的十六进制数字也都比较大。
  • 一个string类型的值会由若干个Unicode字符组成,每个Unicode字符都可以由一个rune类型的值来承载。
  • 这些字符在底层都会被转换成为UTF-8编码值,而这些UTF-8编码值又会以字节序列形式表达和存储。所以,一个string类型的值在底层就是一个能够表达若干UTF-8编码值得字节序列([]byte)
  • 字符串的长度是底层字节切片的长度。
  • 此外,在使用range遍历字符串时,如果字符串中存在需要多个字节来表示的字符(比如中文),那么得到的索引就会跳跃。而另外一个变量是rune类型的。相邻的 Unicode 字符的索引值并不一定是连续的。这取决于前一个 Unicode 字符是否为单字节字符

对于4字节的UTF-8编码,如何判断是两个2字节还是一个1字节一个3字节?

const (
	RuneError = '\uFFFD'     // the "error" Rune or "Unicode replacement character"
	RuneSelf  = 0x80         // characters below Runeself are represented as themselves in a single byte.
	MaxRune   = '\U0010FFFF' // Maximum valid Unicode code point.
	UTFMax    = 4            // maximum number of bytes of a UTF-8 encoded Unicode character.
)

// Code points in the surrogate range are not valid for UTF-8.
const (
	surrogateMin = 0xD800
	surrogateMax = 0xDFFF
)

const (
	t1 = 0x00 // 0000 0000
	tx = 0x80 // 1000 0000
	t2 = 0xC0 // 1100 0000
	t3 = 0xE0 // 1110 0000
	t4 = 0xF0 // 1111 0000
	t5 = 0xF8 // 1111 1000

	maskx = 0x3F // 0011 1111
	mask2 = 0x1F // 0001 1111
	mask3 = 0x0F // 0000 1111
	mask4 = 0x07 // 0000 0111

	rune1Max = 1<<7 - 1
	rune2Max = 1<<11 - 1
	rune3Max = 1<<16 - 1

	// The default lowest and highest continuation byte.
	locb = 0x80 // 1000 0000
	hicb = 0xBF // 1011 1111

	// These names of these constants are chosen to give nice alignment in the
	// table below. The first nibble is an index into acceptRanges or F for
	// special one-byte cases. The second nibble is the Rune length or the
	// Status for the special one-byte case.
	xx = 0xF1 // invalid: size 1
	as = 0xF0 // ASCII: size 1
	s1 = 0x02 // accept 0, size 2
	s2 = 0x13 // accept 1, size 3
	s3 = 0x03 // accept 0, size 3
	s4 = 0x23 // accept 2, size 3
	s5 = 0x34 // accept 3, size 4
	s6 = 0x04 // accept 0, size 4
	s7 = 0x44 // accept 4, size 4
)

// first is information about the first byte in a UTF-8 sequence.
var first = [256]uint8{
	//   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
	//   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
	xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
	s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
	s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
	s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
}


func RuneCountInString(s string) (n int) {
	ns := len(s)
	for i := 0; i < ns; n++ {
		c := s[i]
		if c < RuneSelf {
			// ASCII fast path
			i++
			continue
		}
		x := first[c]
		if x == xx {
			i++ // invalid.
			continue
		}
		size := int(x & 7)
		if i+size > ns {
			i++ // Short or invalid.
			continue
		}
		accept := acceptRanges[x>>4]
		if c := s[i+1]; c < accept.lo || accept.hi < c {
			size = 1
		} else if size == 2 {
		} else if c := s[i+2]; c < locb || hicb < c {
			size = 1
		} else if size == 3 {
		} else if c := s[i+3]; c < locb || hicb < c {
			size = 1
		}
		i += size
	}
	return n
}

你可能感兴趣的:(Golang)