为什么会出现more than one character in rune literal(go语言)
r:=rune('abc'),会报错 more than one character in rune literal
为什么会出现这样一个错误呢?这就得探究 rune这个东西
一、关于rune
你会在 C:\Go\src\builtin\builtin.go 源文件中看到他的定义,他上面是有一段描述,rune是init32的别名,但是不仅仅如此
// 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
// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32
rune的大小,int32范围是 -2147483648 到 2147483647
通过 fmt.Println(unsafe.Sizeof(int32(0))) 可以看到他占用4字节=32bit
在 Go 语言中,rune 是一种特殊的数据类型,用于表示 Unicode 字符,注意是单个字符,比如 \u12e4 。他是一个 unicode code points(码点) , 每个 rune 值对应一个 Unicode Code Point,所以你能够表示所有 Unicode 字符(包括中文、表情符号等多字节字符)。在rune看来一个表情 😀就是一个字符
上面的报错,实际是 go解析器认为rune('abc') 中的a、b、c是3个字符了,所以报错,看下下面示例
r:=rune('\u12e4') fmt.Printf("%d %q %U %v\n", r, r, r,utf8.ValidRune(r))
//4836 'ዤ' U+12E4 true
但是注意: In Go, rune is a built-in type (an alias for int32), not a function. It is used to represent a single Unicode code point.
而语法 rune(x) 是一个类型转换, 他看起来像一个函数的调用,但是他是go内部的一种机制,表示是把数据转换为rune 类型.
既然是rune是类型,那么我们就建立一个rune类型的变量看下:
var ii rune='a' fmt.Printf("ascii码:%d %q %U %v\n", ii, ii, ii,utf8.ValidRune(ii)) //ascii码:97 'a' U+0061 true var ii2 rune="a" fmt.Printf("ascii码:%d %q %U %v\n", ii2, ii2, ii2,utf8.ValidRune(ii2)) //报错,cannot use "a" (type untyped string) as type rune in assignment 类型不匹配 //因为""代表的是字符串 字符串由字节组成,而非单个字符,当你用var s ="str" ; s[0] 这样的索引访问字符串时,你是在单独检索每个字节,而不是完整的Unicode字符。
这在处理多字节字符如表情符号或非英文文本时尤为重要 在go中 用双引号包围的字符串, 他会被自动编码为utf-8,而字节只是原始数据单位,可以表示各种编码格式的文本。索引字符串时,你会得到底层的字节值,而不是字符如上例中的a文本本身。
这与许多其他编程语言不同,后者通常将字符串视为字符序列而非字节。 在Unicode中,Code Points是字符的唯一标识符。 在go中,rune 表示该Code Points为32位整数(int32)。 例如,“⌘”(命令键符号)有一个Unicode Code Points U+2318(\u2318),在go中存储为rune。
当然我们还要了解一下,我们编码都是保存到文件,最终存储是二进制形式,英文系统还好,使用ascii编码即可,美国信息交换标准代码(ASCII)是一组字符集,表示大写字母、小写字母、数字以及各种标点符号和设备控制字符。ASCII的问题在于它不包括世界上大多数字符, 比如汉字和其他不同语言使用的字符集。这一限制促成了Unicode字符的发明。Unicode 是 ASCII 的超集;它包含了当今全局书写系统中存在的所有字符, 并为每个字符分配一个特殊的 Unicode code points。这个Unicode code points在Golang中被称为rune。
文件在读取的时候会按照某个编码方式来显示,这和系统有关,比如windows,你可以在dos界面 上执行 chcp 65001 ,它是Windows命令提示符中用于将当前代码页临时切换到UTF-8编码的命令,通常解决中文乱码问题,让控制台能正确显示多语言字符。输入chcp 65001后按回车,只对当前窗口生效;要永久修改,
Windows多语言支持的文件(主要是MUI资源文件)通常存放在 C:\Windows\System32\ 或 C:\Windows\SysWOW64\ 目录下,格式为 原始文件名.语言代码.mui,例如 explorer.exe.zh-CN.mui。
普通用户主要通过设置时间和语言以及区域等来添加或管理语言包和输入法,系统会自动处理这些底层文件。
MUI (Multilingual User Interface) 文件规范主要指 Windows 系统中的多语言资源文件格式, 其命名通常为 原始文件名.语言代码.mui(如 explorer.exe.zh-CN.mui),用于分离核心程序与语言包,实现软件多语言支持, 主要存储界面文本、图标等资源,而文件本身通常是二进制格式,需特定工具(如资源编辑器)或系统加载来解析。
MUI 和 UTF-8 编码格式的关系是:MUI(移动统一界面/跨平台开发框架)内部处理和传输数据时,通常采用行业标准 UTF-8 编码格式
但是我们用程序读取出来,发给显示器来显示,这个过程,还需要另外一些知识, 屏幕需要根据 Windows Fonts 文件夹 通常在 C:\Windows\Font 目录下
的字库,即.ttc文件 ,来绘制到屏幕上,每个字体文件都有自己的头信息,包括字体的名称、版本、字宽等。包含字体的轮廓信息、位图数据等。
我们使用ps时候,有时候缺字体会copy字体到这里,ps就能打很多精彩的字体样式出来。这些就是字模需要的信息,字模通过字模寻址公式与字符编码对应,用于LCD屏幕上显示。
我们再看下
C:\Go\src\unicode 这个文件夹下,
const ( MaxRune = '\U0010FFFF' // 最大有效的 Unicode code point. ReplacementChar = '\uFFFD' // \uEFFD 代表 invalid code points. MaxASCII = '\u007F' // 最大的 ASCII value. (127/U+007F) MaxLatin1 = '\u00FF' // 最大的 Latin-1 value.(255) \u代表是unicode编码 )
C:\Go\src\unicode\letter.go
看下他们表示的数字和类型
func 输出MaxASCII(){ // U+10FFFF fmt.Printf(" unicode.MaxRune unicode值: %U\n", unicode.MaxRune) //int32 fmt.Printf(" unicode.MaxRune类型: %T\n", unicode.MaxRune) var m int32=unicode.MaxRune //1114111 fmt.Println("unicode.MaxRune 值:", m) //int32 fmt.Printf(" unicode.MaxASCII的类型: %U\n", unicode.MaxASCII) // U+007F fmt.Printf(" unicode.MaxASCII unicode值: %T\n", unicode.MaxASCII) var i int32=unicode.MaxASCII //127 fmt.Println("unicode.MaxASCII值:", i) }
C:\Go\src\unicode\utf8\utf8.go 文件中定义了很多相关rune的配置
// Numbers fundamental to the encoding. const ( RuneError = '\uFFFD' // the "error" Rune or "Unicode replacement character" //65533 RuneSelf = 0x80 // characters below RuneSelf are represented as themselves in a single byte. //unicode range \u0000-\U0010ffff MaxRune = '\U0010FFFF' // 有效的最大Unicode code point. //https://codepoints.net/U+10FFFF UTFMax = 4 // UTF-8 编码 Unicode character 字符集字节数是4个字节(32bit). ) // Code points in the surrogate range are not valid for UTF-8. const ( //utf-8 range 0xD800-0xDFFF surrogateMin = 0xD800 surrogateMax = 0xDFFF )
//下面写段代码看下 \U0010FFFF的值情况
var ii rune='\U0010FFFF' fmt.Printf(" %d %q %+q %U %v\n", ii, ii,ii, ii,utf8.ValidRune(ii)) //1114111 '\U0010ffff' '\U0010ffff' U+10FFFF true
我们看一下 rune(0)、rune('0') 差别(注意和空不是一个概念,rune不允许为空,rune默认值为 \x00)
| Unicode Character | Unicode Code Point | Decimal Value |
|---|---|---|
| 0 | U+0030 |
48 |
| A | U+0041 |
65 |
| a | U+0061 |
97 |
| ¿ | U+00BF |
191 |
| π | U+03C0 |
960 |
| 🧠 | U+1F9E0 |
129504 |
UTF-8编码有以下几种格式:
U-00000000 – U-0000007F: 0xxxxxxx (这个范围是ASCII码,也就是rune是包括ascii码,U-0000007F 即十进制127,所以utf-8也是兼容ascii码的)
U-00000080 – U-000007FF: 110xxxxx 10xxxxxx (00000080即128)
U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx (00000800 即2048)
U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
可以看下 Unicode – The World Standard for Text and Emoji https://home.unicode.org/
fmt.Printf("rune('0'): %d %c %U %v %T %+q\n", rune('0'),rune('0'), rune('0'), rune('0'),rune('0'),rune('0')) fmt.Printf("rune(0): %d %c %U %v %T %+q\n", rune(0), rune(0),rune(0),rune(0),rune(0),rune(0)) fmt.Printf("rune(1): %d %c %U %v %T %+q\n", rune(1), rune(1),rune(1),rune(1),rune(1),rune(1)) /* rune('0'): 48 0 U+0030 48 int32 '0' rune(0): 0 U+0000 0 int32 '\x00' rune(1): 1 ╔ U+0001 1 int32 '\x01'
%U 代表的是 unicode code points
$+q 显示的是\u uicode编码形式
%c 显示unicode字符 */
//fmt.Printf("%c", '48') 错误,大于1个字符
//fmt.Printf("%d %c", '\0','\0') 错误,\
fmt.Printf("%d %c %U\n", '0','0','0') //48 0 (48为ascii码值)
fmt.Printf("%d %c %U\n", 'A','A','A') //65 A (65为ascii码值)
fmt.Printf("%d %c %U\n", "A","A","A") //得不到结果,用的是双引号
fmt.Printf("%d %c %U\n", 'a','a','a') //97 a
fmt.Printf("%d %c %U\n", '1','1','1') //49 1 U+0031
fmt.Printf("%d %c %U\n", 1,1,1) //1 ╔ U+0001
/*
输出结果;
48 0 U+0030
65 A U+0041
%!d(string=A) %!c(string=A) %!U(string=A)
97 a U+0061
49 1 U+0031
1 ╔ U+0001
*/
utf8字节流,底层存储
下面我们从go、python以及od程序三个角度来分析字节流
/* Unicode编码规范通常使用十六进制表示法来表示Unicode代码点(code points)的整数值,并提供了三种不同的编码格式,即:UTF-8、UTF-16 和 UTF-32。 1.\uXXXX 4位十六进制 \u0041 代表 'A' \u5f20 代表 '张' \u20ac 代表 '€' (欧元符号) 用于常用字符、ASCII、大部分中文等 2.\UXXXXXXXX 8位十六进制 \U0001F600 代表 '😀' (笑脸表情) \U0001F4A9 代表 '💩' (大便表情) 用于包含表情符号 (Emoji)、生僻字等的高位码位字符 3.\x表示法:
\x后跟两位十六进制数(例如 \xEF)用来表示一个字节的原始值,,常用于显示非ASCII字符或二进制数据,我们存储到文本文件中的就是这种类型数据 */ func utf8字节流(){ str := "A张€" fmt.Printf(" [%q]\n", str) //["A 张 €"] //输出rune单个字符 fmt.Printf("unicode字符: %c\n", []rune(str)) //[A 张 €] //十六进制 fmt.Printf("unicode编码: %x\n", []rune(str)) //[41 5f20 20ac] //字节存储,文件中存储的格式,一个中文对应3个字节(utf编码) fmt.Printf("utf-8编码 : [% x]\n", []byte(str)) //[41 e5 bc a0 e2 82 ac] /* 输出结果,我们看下对应内容 unicode字符: [A 张 € ] unicode编码: [41 5f20 20ac ] utf-8编码 : [41 e5 bc a0 e2 82 ac] 说明: 1.unicode字符 "张" unicode字符 张 unicode编码 5f20 (\u5f20) utf8字节编码 e5 bc a0 (\xe5\xbc\xa0) 2.unicode字符 "€" € 20ac e2 82 ac */ //我们再反过来测试下 //1.unicode字符 A:='\u0041' //\uXXXX形式 fmt.Printf("% c\n", A) B:='\u5f20' fmt.Printf("% c\n", B) //2.把存储的字节转为unicode字符 const C = "\xe5\xbc\xa0" fmt.Printf("%q\n", C) //"张" const D = "\xe2\x82\xac" fmt.Printf("%q\n", D) //"€" /* 我们利用python来看下 print('A'.encode('utf-8')) #b'A' print('张'.encode('utf-8')) #b'\xe5\xbc\xa0' print('€'.encode('utf-8')) #b'\xe5\xbc\xa0' r"""
输出: b'A' b'\xe5\xbc\xa0' b'\xe2\x82\xac' """ */ /* 利用cygwin //建立个文件a.txt,输入123abc中한,你右键查看文件属性会,显示文件大小为12字节 $ od -t x1 a.txt 0000000 31 32 33 61 62 63 e4 b8 ad ed 95 9c 0000014 od程序语法: od [-A 地址进制] [-t 显示格式] 文件 选项介绍: -A 地址进制: 按指定的进制显示地址信息; -t 显示格式: 指定数据的显示格式; -A指定地址进制包括: o 八进制(系统默认值) d 十进制 x 十六进制 n 不打印位移值 -t指定数据的显示格式,主要参数有: c ASCII字符或反斜杠序列(如\n) d 有符号十进制数 f 浮点数 o 八进制(系统默认值) u 无符号十进制数 x 十六进制数 例如: od -t x1 -N a.txt 查看前4个字节的十六进制 od -c a.txt # 查看字符编码(UTF-8 的汉字会显示为三个八进制值) od 是一个强大的字节流分析工具,将原始数据翻译成我们能理解的格式 */
//上面是我们利用od输出的内容,我们把结果反过来看下
const E = "\x31\x32\x33" //十六进制数字utf-8编码
fmt.Printf("%q\n", E) //输出"123"
const F = "\x61\x62\x63" //acsii码(utf-8编码),61对应的a,62对应b,63对应c
//上面的文本”123abc”被编码成字节流存储到a.txt中,所谓字节流就是十六进制的数字
const k = "\x61\x62\x63"
fmt.Printf("%q\n", k) //输出abc
const g ="\xed\x95\x9c"
fmt.Printf("%q\n", g) //输出한,我们也可以看到韩文也是3个字节对应一个韩文字符
//当然我们知道,磁盘上的文件存储实际都是以二进制格式来存储的,只是由于编码不同,从而可能造成乱码情况的出现。
}
关于U0010FFFF和 \uFFFD
来自:C:\Go\src\unicode\utf8\utf8.go
const ( RuneError = '\uFFFD' // 代表的不是有效的Rune或是一个无效的unicode码点(code point) ,请看letter.go中ReplacementChar RuneSelf = 0x80 // 0x80=128, 使用时一般0x80-1(127) ,表示单个字节rune本身 //unicode range \u0000-\U0010ffff //\U0010FFFF 1114111 ,代表最大的unicode码点 MaxRune = '\U0010FFFF' // Maximum valid Unicode code point. //https://home.unicode.org//U+10FFFF UTFMax = 4 // maximum number of bytes of a UTF-8 encoded Unicode character.(utf-8是可变编码,所以是字节是可变的,1->4字节) ) 请参考: https://en.wikipedia.org/wiki/Plane_(Unicode)
Unicode 平面和使用的码点范围(来自: Plane (Unicode) - Wikipedia)
Unicode 共有 17 个平面,BMP 是Unicode 的 0 号平面,即 [U+0000, U+FFFF] 区间。
| Basic | Supplementary | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Plane 0 | Plane 1 | Plane 2 | Plane 3 | Planes 4–13 | Plane 14 | Planes 15–16 | |||
| 0000–FFFF(65535) | 10000–1FFFF | 20000–2FFFF | 30000–3FFFF | 40000–DFFFF | E0000–EFFFF | F0000–10FFFF | |||
| Basic Multilingual Plane | Supplementary Multilingual Plane | Supplementary Ideographic Plane | Tertiary Ideographic Plane | unassigned | Supplementary Special-purpose Plane | Supplementary Private Use Area planes | |||
| BMP | SMP | SIP | TIP | — | SSP | SPUA-A/B | |||
|
0000–0FFF |
8000–8FFF |
10000–10FFF |
18000–18FFF |
20000–20FFF |
28000–28FFF |
15: SPUA-A |
|||
关于字节数字符数 \u和\U的区别
func 关于字节数字符数(){ /* str:='\u0xc2' .\fint8讲究.go:879:11: invalid character 'x' in hexadecimal escape //0x代表的是16进制的标识,所以在rune中是无效字符,如果想要把它转为 rune类型, str:="\u0xc2",用双引号来代替单引号,再使用 DecodeRuneInString转成rune类型数据 */ hex16:=0xc2 fmt.Printf("% d\n", hex16) //194 chaifen:=0x4E59 //4E 59 在Windows上从高字节开始读取,在Mac上读取时是从低字节开始,Mac OS是会把4E 59 编码为59 4E,所以要注意高低的前后关系,也就是大字节序和小字节序 //4E59 来自 https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E7%A0%81 fmt.Printf("% c\n", chaifen) /* str:='0xc2' .\fint8讲究.go:886:5: cannot use "" (type untyped string) as type rune in assignment //因为0xc2,在rune中代表4个字符,你可以看下,下面的 runecounttests 变量的测试... */ //下面是来自 https://tip.golang.org/src/unicode/utf8/utf8_test.go ,本地也有 var runecounttests = []string{ "abcd", //4个字符 "☺☻☹", //3个字符 "1,2,3,4", //7个字符 "\xe2\x00", //2个字符 "\xe2\x80", //2个字符 "a\xe2\x80", //3个字符 //下面是我增加的 "\uFFFD", //1 "0x80", //4 "\U0010FFFF", //1 } //下划线 _代表忽略key for _, tt := range runecounttests { fmt.Printf("字符数量:%d\n",utf8.RuneCountInString(tt)) } /* 字符数量:3 字符数量:7 字符数量:2 字符数量:2 字符数量:3 */ /* \u和\U的区别 在 Golang 中,\u 和 \U 都是用来表示 Unicode 字符的转义序列,区别在于它们所能表示的码位(Code Point)范围: \uXXXX 表示一个 16 位的 Unicode 码位(最多能表示 2^16 个字符), 而 \UXXXXXXXX 表示一个 32 位的 Unicode 码位,\U 后面必须跟 8 位十六进制数字 1.\uXXXX 4位十六进制 \u0041 代表 'A' \u5f20 代表 '张' \u20AC 代表 '€' (欧元符号) 用于常用字符、ASCII、大部分中文等 2.\UXXXXXXXX 8位十六进制 \U0001F600 代表 '😀' (笑脸表情) \U0001F4A9 代表 '💩' (大便表情) 用于包含表情符号 (Emoji)、生僻字等的高位码位字符 */ strnum:='\u0080' fmt.Printf("str80: %032b\n", strnum) ejword := "😊" fmt.Println(len(ejword)) // 4 bytes fmt.Printf("ejword为:%U\n", []rune(ejword)) //ejword为:[U+1F60A] fmt.Printf("% x\n", ejword) // %x十六进制 f0 9f 98 8a fmt.Println([]rune(ejword)) // [128522] //反过来就是 : [U+1F60A],\U表示 word2:='\U0001F60A' //注意1F60A前要加000, 而\U1F60A这种是错误写法(必须是8位的16进制) fmt.Printf("% c\n", word2) //输出: 😊 str16:='\U0010FFFF' fmt.Printf("str16为:%d\n", str16) //str类型为:int(32位) //str16为:1114111 stremoji:='\U0001F600' fmt.Printf("stremoji为:%c\n", stremoji) //stremoji为:😀 const( s='\U0010FFFF' ) s1:=s+1 fmt.Printf(":%d\n", s1) const ( runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character" runeSelf = 0x80 // characters below RuneSelf are represented as themselves in a single byte. //unicode range \u0000-\U0010ffff //\U0010FFFF 1114111 maxRune = '\U0010FFFF' // Maximum valid Unicode code point. //https://home.unicode.org//U+10FFFF uTFMax = 4 // maximum number of bytes of a UTF-8 encoded Unicode character. ) runestr:=runeSelf fmt.Printf("str类型为:%T\n", runestr) //str类型为:int(32位) fmt.Printf("实际字节大小:%d 十进制:%d 二进制:%032b 十六进制:%x unicode : %U\n",len(string(runestr)),runestr, runestr,runestr,runestr) //实际字节大小:2 十进制:128 二进制:00000000000000000000000010000000 十六进制:80 unicode : U+0080(\u0080) //len() 函数返回的是字符串的字节数,而不是字符(Unicode 码点)数。 //字符'A': str:='A' fmt.Printf("str类型为:%T\n", str) //str类型为:int32 //str由于是int32类型即rune类型,那么他最多可以表示32位数据,但是存储时用的是utf-8编码,利用其变长的策略 fmt.Printf("%c Unicode码点:%U\n", str, str) //A Unicode码点:U+0041 fmt.Printf("字节大小:%d 十进制:%d 二进制:%08b 十六进制:%x\n",len(string(str)),str, str,str) //字节大小:1 十进制:65 二进制:01000001 十六进制:41(0x41) //关于字节数 strbytecount := "h中国g" // 2个汉字,UTF-8编码下每个汉字占3个字节,共8个字节 fmt.Println(len(strbytecount)) // 输出字节数: 8 //len(str) 返回的字节数就是字符串数据在内存中的大致大小 //当然实际占用内存: len(str)(加上 Go 字符串结构体开销) //关于字符数 runcountstr := "h中国g" fmt.Printf("字符数量:%d",utf8.RuneCountInString(runcountstr)) // 输出字符数量: 4 //RuneCountInString函数负责统计按照unicode编码,统计出的字符数 //string类型的大小 fmt.Printf("字符串类型大小;%d\n",unsafe.Sizeof(strbytecount)) // 占用16字节 //总结就是表示和存储时分离的,上面的str只占了1个字节,但是str是可以表示4个字节数据,如果str:='中',则存储时占据3个字节. //rune是int32,string是int64 //就好比有个房间可以放16个箱子,但是实际只放了1个箱子,每个箱子里面有8个格子,每个格子里面只能放红球或黑球 }
下面是检查是否rune有效
//下面的变量值: 来自 C:\Go\src\unicode\utf8\utf8.go func 模拟utf8文件中的ValidRune(){ const ( //utf-8 0xD800-0xDFFF 表示的是此区间码点,不符合utf-8标准 surrogateMin = 0xD800 //55296 surrogateMax = 0xDFFF //57343 //utf-8最大是 maxRune ,但是是 0-0xD800 和0xDFFF-\U0010FFFF 区间的数据 ) const ( //最大有效的 Unicode code point //U+0010FFFF = 1114111(十进制数) maxRune = '\U0010FFFF' ) //这段测试的目的是看rune的范围,从段也可以看出rune要刨掉 surrogateMin-surrogateMax 之间的utf-8码点 //0xD800=55296,0xDFFF=57343 //valid:= rune(55296-1) //有效 valid:= rune(0xC2) //有效 i:=0 switch { case 0 <= valid && valid < surrogateMin: i= 1 case surrogateMax < valid && valid <= maxRune: //这个翻译就是 surrogateMax<valid<maxRune //即valid变量要在0xD800-0xDFFF之间 i=1 } //测试超过范围 //invalid:= rune(0xfffffff) //268435455, 不符合 invalid:= rune(55296+1) //无效 j:=0 if invalid > surrogateMin && invalid < surrogateMax { j= 1 //有效 } //i if i ==1{ fmt.Println("有效utf-8字符,是有效的rune") } //j if j ==1{ fmt.Println("不是有效utf-8字符,是无效的rune") } }
使用utf8中定义的ValidRune函数来检测是否有效的rune
//\u代表是unicode编码 slice := []rune{'\u0041', '\u0042', '\u0043', '\u20AC', '\uFFFD', 0xfffffff, -1} for i, v := range slice { fmt.Printf("%d %q %v\n", i, v, utf8.ValidRune(v)) }
utf8.ValidRune 是在 C:\Go\src\unicode\utf8\utf8.go 中定义
可以看下这里不少这块的例子: C:\Go\src\unicode\utf8\utf8_test.go
rune不能为nil
//RuneError = '\uFFFD' func 检测RuneError(){ //在Go 语言中,未初始化的 []byte 变量的默认值是nil const runeError = '\uFFFD' var bArr []byte if bArr == nil { fmt.Println("字节数组为nil") } if len(bArr) == 0 { fmt.Println("字节数组为空") } else { fmt.Println("字节数组不为空") } n := len(bArr) if n < 1 { fmt.Println("是无效的unicode字符或错误的Rune") } //我们模拟下DecodeRune的第1-4行 n1 := len(bArr) if n1 < 1 { fmt.Println("RuneError错误") } //把[]byte转成string,再用[]rune转成rune b_s_r:=[]rune(string(bArr)) for _, r := range b_s_r { result:=utf8.ValidRune(r) if result !=true { fmt.Println("RuneError错误") }else{ fmt.Println("是有效的rune") } } /* //从这里可以看到,rune起码要=0,但不能为空数据 func ValidRune(r rune) bool { switch { case 0 <= r && r < surrogateMin: return true case surrogateMax < r && r <= MaxRune: return true } return false } */ //也就是rune不允许为空. //r:=rune("") //result:=utf8.ValidRune(r) //fmt.Printf(result==true) }
rune的默认值
func 默认值(){ var i int var f float64 var b bool var s string var r rune fmt.Printf("%v %v %v %q %q\n", i, f, b, s,r) fmt.Printf("r默认值:%c",r) /* 0 0 false "" '\x00' */
}
检查是否是ascii码
//https://pkg.go.dev/unicode 有效的unicode表 //https://golang.google.cn/pkg/unicode/ /* const ( MaxRune = '\U0010FFFF' // Maximum valid Unicode code point. ReplacementChar = '\uFFFD' // Represents invalid code points. MaxASCII = '\u007F' // maximum ASCII value. 127 MaxLatin1 = '\u00FF' // maximum Latin-1 value. 255(127+96),96是后期补充到Latin-1,前127即ascii码
//变量来自 C:\Go\src\unicode\letter.go ) */ func isASCIIRange(){ var s string="😊abc" for _, c := range s { if c > unicode.MaxASCII { fmt.Printf("%c 不是ASCII,但是是unicode字符: \n", c) }else{ fmt.Printf("unicode字符:%c\n", c) } } /* 输出 😊 不是ASCII,但是是unicode字符: unicode字符:a unicode字符:b unicode字符:c */ }
变量来自 C:\Go\src\unicode\letter.go
func 输出unicode_lettergo(){ // U+10FFFF fmt.Printf(" unicode.MaxRune unicode值: %U\n", unicode.MaxRune) //int32 fmt.Printf(" unicode.MaxRune类型: %T\n", unicode.MaxRune) var m int32=unicode.MaxRune //1114111 fmt.Println("unicode.MaxRune 值:", m) //int32 fmt.Printf(" unicode.MaxASCII的类型: %U\n", unicode.MaxASCII) //U+007F fmt.Printf(" unicode.MaxASCII unicode值: %T\n", unicode.MaxASCII) var i int32=unicode.MaxASCII //127 fmt.Println("unicode.MaxASCII值:", i) }
参考:
https://pkg.go.dev/unicode/utf8 官方的example 也utf8_test.go例子还不太一样,而且可以在线例子直接可以修改和测试
https://akaedu.github.io/book/apas02.html
https://www.evertype.com/standards/csur/klingon.html
https://man7.org/linux/man-pages/man7/ascii.7.html (ascii码表,比较全)
https://man7.org/linux/man-pages/man7/utf-8.7.html
https://go.dev/tour/basics/12 (在线编写go代码)
C:\Go\src\unicode\utf8\example_test.go 源码对utf8包里面的函数几乎都有测试
https://medium.com/@AlexanderObregon/rune-vs-byte-in-go-string-operations-780c0f2dd92c

浙公网安备 33010602011771号