怎么使用织梦做网站,北京网站制作设计公司排名,网站编程入门教程,网站编辑难做吗1、 Golang 字符编码
Golang 的代码是由 Unicode 字符组成的#xff0c;并由 Unicode 编码规范中的 UTF-8 编码格式进行编码并存储。
Unicode 是编码字符集#xff0c;囊括了当今世界使用的全部语言和符号的字符。有三种编码形式#xff1a;UTF-8#xff0c;UTF-16#…1、 Golang 字符编码
Golang 的代码是由 Unicode 字符组成的并由 Unicode 编码规范中的 UTF-8 编码格式进行编码并存储。
Unicode 是编码字符集囊括了当今世界使用的全部语言和符号的字符。有三种编码形式UTF-8UTF-16UTF-32。UTF: Unicode Transformation Format统一码转换格式
在这几种编码格式的名称中- 右边的整数的含义是以多少个比特作为一个编码单元。以 UTF-8 为例它会以 8 个比特也就是一个字节作为一个编码单元。并且它与标准的 ASCII 编码是完全兼容的。也就是说在 [0x00, 0x7F]的范围内这两种编码表示的字符都是相同的这也是 UTF-8 编码格式的一个巨大优势这里不探讨 UTF-16 及 UTF-32。
UTF-8 是一种可变长的编码方案。换句话说它会用一个或多个字节来表示某个字符最多使用四个字节。比如对于一个英文字符它仅用一个字节就可以表示而对于一个中文字符它需要使用三个字节才能够表示。不论怎样一个受支持的字符总是可以由 UTF-8 编码为一个字节序列。以下会简称后者为 UTF-8 编码值。 从上图可知 UTF-8 的编码方式
什么时候读1个字节的字符 字节的第一位为0后面7位为符号的unicode码。所以这样看英语字母的utf-8和ascii一致。 什么时候读多个字节的字符 对于有n个字节的字符n1…. 其中第一个字节的高n位就为1换句话说 第一个字节读到0那就是读1个字节第一个字节读到n个1就要读n个字节
0xxxxxxx # 读1个字节
110xxxxx 10xxxxxx # 读2个字节
1110xxxx 10xxxxxx 10xxxxxx #读3个字节
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx #读4个字节Unicode符号范围 | UTF-8编码方式
(十六进制) | 二进制
------------------ ----------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx那 Unicode 是如何填充UTF-8各个字节的呢
比如 码 这个汉字对应的 unicode编码为 U7801
对应的十六进制处于 0000 0800-0000 FFFF 中也就是 3 个字节相应的二进制为 1110xxxx 10xxxxxx 10xxxxxx码 的 unicode编码为 U7801 对应的二进制为 111100000000001为了和接下来填充字节方便这里做个格式优化 111 100000 000001从后向前填充高位不够的补0000001 填充第三个字节从左往右数10000001100000 填充第二个字节 10100000111 填充第一个字节高位不够的就补0为 11100111最终结果为 11100111 10100000 10000001对应的十六进制分别对应 e7 a0 81
func TestInt(t *testing.T) {s1 : 码for i : 0; i len(s1); i {fmt.Printf(%x , s1[i])}
}打印的结果为 e7 a0 81和上面演算的一致。
2、string 数据结构
先来看看 Golang 的 string 的数据结构
type StringHeader struct {Data uintptrLen int
}其中包含指向字节数组的指针 Data 和数组的大小 Len后者 Len 方便在 len() 时可以 O(1) 时间给出大小就是常见的以空间换时间。字符串由字符组成字符的底层由字节组成而一个字符串在底层的表示是一个字节序列这个字节序列就存储在 Data 里不过是只读的。
import (fmttesting
)func TestStr(t *testing.T) {str : Hello Worldfmt.Println(str)
}把上面代码 go tool compile -S str_test.go str_test.S 生成汇编代码然后找到
go.string.Hello World SRODATA dupok size110x0000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World能够看到 Hello World 旁有一个 SRODATA 的标记在 Golang 中编译器会将只读数据标记成 SRODATA。
再来看看 slice 的数据结构
type SliceHeader struct {Data uintptrLen intCap int
}相比 string 多了个 Cap因此在 Golang 中字符串实际上是只读的字节切片。
那么对于只读的 string若是想要改值应该怎么弄呢?
func TestModifyString(t *testing.T) {str : golang编程l : []byte(str)l[0] Gfmt.Println(string(l)) // Golang编程
}转成相应的字节数组然后以索引的形式更新值。
3、string 编码方式
前面说过字符串由字符组成字符的底层由字节组成而一个字符串在底层的表示是一个字节序列。在 Golang 中字符可以被分成两种类型处理对占 1 个字节的英文类字符可以使用 byte或者 unit8对占 1 ~ 4 个字节的其他字符可以使用 rune或者int32如中文、特殊符号等。
// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte uint8// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune int32可以看到 byte 、 rune 其实分别就是 uint8、int32 的别名byte 占 1 个字节 rune 占 4个字节。
func TestStrLen(t *testing.T) {str1 : gostr2 : go编程fmt.Printf(%v len is %d\n, str1, len(str1))fmt.Printf(%v len is %d\n, str2, len(str2))}运行后发现 str1 长度为 2 这个没问题但 str2 的长度不是 4 而是 8这是什么原因呢
先不着急找答案看看下面的代码
func printBytes(s string) {fmt.Printf(Bytes: )for i : 0; i len(s); i {fmt.Printf(%x , s[i]) // 按十六进制输出}fmt.Printf(\n)
}func printChars(s string) {fmt.Printf(Charaters: )for i : 0; i len(s); i {fmt.Printf(%c , s[i]) // 将数字转换成它对应的 Unicode 字符}fmt.Printf(\n)
}func TestInt(t *testing.T) {s1 : go编程fmt.Printf(s1: %s, bytes len(s1)%d\n, s1, len(s1))fmt.Printf(s1: %s, rune len(s1)%d\n, s1, len([]rune(s1)))printBytes(s1)printChars(s1)
}运行后打印如下
s1: go编程, bytes len(s1)8
s1: go编程, rune len(s1)4
Bytes: 67 6f e7 bc 96 e7 a8 8b
Charaters: g o ç ¼ – ç ¨仔细看发现 rune 类型的输出了 4另外 printChars 输出乱码了。
先来看看 rune 类型是 int32 的别名也就是说一个 rune 类型的值会由 4 个字节宽度的空间来存储。它的存储空间总是能够存下一个 UTF-8 编码值。一个 rune 类型的值在底层其实就是一个 UTF-8 编码值。前者是便于我们人类理解的外部展现后者是便于计算机系统理解的内在表达。
Golang 中常用 rune 类型来处理中文。printChars 之所以输出乱码是因为在第一节中提到的在 UTF-8 中汉字是以三个字节存储的len() 是按单字节来计算长度因此对于三个字节的中文来说输出三分之铁定乱码。那么如何输出才不乱码呢
func TestRune(t *testing.T) {str : golang编程l : []rune(str)for i : 0; i len(l); i {fmt.Printf(%c , l[i])}
}打印输出 g o l a n g 编 程。
当然了还可以使用 for range 来打印字符串里的中文。
func TestRange(t *testing.T) {str : golang编程for i, s : range str {fmt.Printf(%d: %c\n, i, s)}
}打印输出
0: g
1: o
2: l
3: a
4: n
5: g
6: 编
9: 程那为什么会这样呢原因就在 Golang 中会把 for range 结构转换成如下所示的形式 // Transform string range statements like for v1, v2 range a intoha : afor hv1 : 0; hv1 len(ha); {hv1t : hv1hv2 : rune(ha[hv1])if hv2 utf8.RuneSelf {hv1} else {hv2, hv1 decoderune(ha, hv1)}v1, v2 hv1t, hv2// original body}for range 循环在迭代字符串时会逐个处理字符串中的 Unicode 码点rune而不是字节。由于 Golang 的原生字符串类型是以 UTF-8 编码的UTF-8 是一种能够表示 Unicode 码点的变长编码方式for range 循环能够正确处理这种编码。
通俗点就是 for range 会先把被遍历的字符串值拆成一个字节序列然后再试图找出这个字节序列中包含的每一个 UTF-8 编码值或者说每一个 Unicode字符。
func TestRange(t *testing.T) {str : golang编程for i, s : range str {fmt.Printf(%d: %c [% x]\n, i, s, []byte(string(s)))}
}打印输出
0: g [67]
1: o [6f]
2: l [6c]
3: a [61]
4: n [6e]
5: g [67]
6: 编 [e7 bc 96]
9: 程 [e7 a8 8b]由此可以看出字符串中相邻 Unicode 字符的索引值不一定是连续的。 这取决于前一个 Unicode 字符是否为单字节字符(byte)。Golang 中的一个 string 类型值会由若干个 Unicode 字符组成每个 Unicode 字符都可以由一个 rune 类型的值来承载。这些字符在底层都会被转换为 UTF-8 编码值而这些 UTF-8 编码值又会以字节序列的形式表达和存储。因此一个string 类型的值在底层就是一个能够表达若干个 UTF-8 编码值的字节序列。
ok到这里了发现两种不同的 for 循环在输出字符串的字符时会有所不同这里做个归类
for-standalone 会遍历字符串的每一个字节(Byte类型)在遇到字符串中有汉字时会乱码for-range 会遍历字符串的每一个 Unicode 字符(Rune 类型) 在遇到字符串中有汉字时不会乱码
最后说说 string、byte 和 rune 三者之间的关系。
string 在底层的表示是由单个字节组成的只读的字节序列Golang 的字符串是以 UTF-8 编码存储的这意味着它们可以包含任意的 Unicode 字符。Golang 把字符分 byte 和 rune 两种类型处理。byte 是类型 unit8 的别名用于存放占 1 个字节的 ASCII 字符如英文字符返回的是字符原始字节。由于 Golang 的字符串是以 UTF-8 编码的一个 byte 可能表示一个字符的一部分对于多字节字符如中文字符也可能表示一个完整的字符对于 ASCII 字符。rune 是类型 int32 的别名用于存放多字节字符如占 3 字节的中文字符返回的是字符 Unicode 码点值(或者说它代表一个 Unicode 码点)。在处理字符串时rune 用于表示字符串中的一个完整的 Unicode 字符无论这个字符是由多少个字节组成的。rune 类型的变量可以存储任何 Unicode 字符包括那些由多个字节表示的字符。
等等等等到这里不妨再多看看。那么如果计算一个字符串的长度呢用自带的 len() 函数对于单字节的字符串来说是准确的若是带有中文字符这种多字节的字符串就不准确了这时除了自己造轮子外其实可以用 Golang 内置的 utf8.RuneCountInString 来统计。
func TestCountStr(t *testing.T) {str : golang编程fmt.Println(utf8.RuneCountInString(str)) // 8
}有兴趣的读者可以看看其内部实现。
// RuneCountInString is like RuneCount but its input is a string.
func RuneCountInString(s string) (n int) {ns : len(s)for i : 0; i ns; n {c : s[i]if c RuneSelf {// ASCII fast pathicontinue}x : first[c]if x xx {i // invalid.continue}size : int(x 7)if isize ns {i // Short or invalid.continue}accept : acceptRanges[x4]if c : s[i1]; c accept.lo || accept.hi c {size 1} else if size 2 {} else if c : s[i2]; c locb || hicb c {size 1} else if size 3 {} else if c : s[i3]; c locb || hicb c {size 1}i size}return n
}