浅谈go语言中的符文字符处理工具

本文涉及的产品
可观测可视化 Grafana 版,10个用户账号 1个月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: 【5月更文挑战第20天】本文简述了Go 1.20之后的rune符文处理工具和函数,`unsafe`包新增了SliceData、String和StringData函数,支持直接将slice转换为array,明确了数组和结构体比较顺序。

1 简介

go1.20 的unsafe包添加了功能SliceData、String和StringData 。

它们完成了独立于实现的切片和字符串操作的函数集,我们一探究竟。

question_ans.png

Go 的类型转换规则已扩展为允许 从 slice 直接转换为 array。

语言规范现在定义了比较数组元素和结构字段的确切顺序。
这阐明了在比较过程中出现恐慌时会发生什么。

func SliceData(slice []ArbitraryType) *ArbitraryType

具体如何实现的,无法得知。 我们可以从使用者角度看它的性能与之前的做法有何变化。

2 编码utf8内容

这里说的所谓符文,也就是 rune,在前文有提到过。

RuneError==unicode.ReplacementChar和 MaxRune==unicode.MaxRune在测试中得到了验证。
在本地定义它们可以避免这个包依赖于unicode包。

  • 编码基础。

    "错误 "符文或 "Unicode替换字符"

         RuneError = '\uFFFD'      
    

    RuneSelf以下的字符在一个字节中表示为它们自己。

     RuneSelf  = 0x80         
    

    最大的有效Unicode码位。

     MaxRune   = '\U0010FFFF'
    

    一个UTF-8编码的Unicode字符的最大字节数。

     UTFMax    = 4            
    

    以下代号范围内的代码点对UTF-8是无效的。

     surrogateMin = 0xD800
     surrogateMax = 0xDFFF
    

    默认的最低和最高延续字节。

     locb = 0b10000000
     hicb = 0b10111111
    

这些常量的名称是为了在下面的表格中提供良好的排列,下面的表格中。
第一个小数点是acceptRanges的索引,或F用于特殊的单字节情况。

特殊的一个字节的情况。第二个小数点是符文长度或特殊的单字节情况下的状态。

acceptRange给出了UTF-8中第二个字节的有效值范围,序列中第二个字节的有效值范围。

    var acceptRanges = [16]acceptRange{
        0: {locb, hicb},
        1: {0xA0, hicb},
        2: {locb, 0x9F},
        3: {0x90, hicb},
        4: {locb, 0x8F},
    }

3 FullRune返回参数

FullRune函数的返回值p中的字节是否以一个完整的UTF-8编码的符文开始。

一个无效的编码被认为是一个完整的符文,因为它将转换为一个宽度为1的错误符文 rune。

func FullRune(p []byte) bool {...}

实例:

    buf := []byte{228, 184, 150} // 世
    fmt.Println(utf8.FullRune(buf))
    fmt.Println(utf8.FullRune(buf[:2]))
    // Output:
    // true
    // false

4 对比FullRuneInString与FullRune函数

这两个函数类似,但FullRuneInString输入是一个字符串。

func FullRuneInString(s string) bool {...}

实例:

    str := "世"
    fmt.Println(utf8.FullRuneInString(str))
    fmt.Println(utf8.FullRuneInString(str[:2]))
    // Output:
    // true
    // false

5 DecodeRune 解压编码

解压参数p中的第一个UTF-8编码,并返回符文和它的宽度(字节)。

如果p是空的,则返回(RuneError, 0)。否则,如果编码是无效的,则返回(RuneError, 1)。
这两种情况都是不可能的正确的、非空的UTF-8的结果。

如果一个编码是不正确的UTF-8,或者编码的符文是编码但是超出了范围,或者不是最短的UTF-8编码。
那么编码是无效的。不进行其他验证。

下面的代码模拟了对x == xx的额外检查,并相应地处理ASCII和无效的情况。
这种掩码和或其他的方法可以防止出现额外的分支。

func DecodeRune(p []byte) (r rune, size int) {...}

实例:

    b := []byte("Hello, 世界")
    for len(b) > 0 {
        r, size := utf8.DecodeRune(b)
        fmt.Printf("%c %v\n", r, size)
        b = b[size:]
    }
    // Output:
    // H 1
    // e 1
    // l 1
    // l 1
    // o 1
    // , 1
    //   1
    // 世 3
    // 界 3

6 对比DecodeRuneInString与DecodeRune

两个函数类似,DecodeRuneInString其输入是一个字符串。

如果s是是空的,它会返回(RuneError, 0)。否则,如果编码是无效的,它会返回(RuneError, 1)。

对于正确的、非空的UTF-8来说,这两种结果都是不可能的。

如果一个编码是不正确的UTF-8,编码的符文是编码但是超出了范围,或者不是最短的UTF-8编码。
那么传入的编码是无效的。不进行其他验证。

func DecodeRuneInString(s string) (r rune, size int) {...}

    str := "Hello, 世界"
    for len(str) > 0 {
        r, size := utf8.DecodeRuneInString(str)
        fmt.Printf("%c %v\n", r, size)
        str = str[size:]
    }
    // Output:
    // H 1
    // e 1
    // l 1
    // l 1
    // o 1
    // , 1
    //   1
    // 世 3
    // 界 3

7 DecodeLastRune解压参数

该解码函数的p中的最后一个UTF-8编码并返回符文和其宽度(字节)。

如果参数p是空的,则返回(RuneError, 0)。

否则,如果参数p的编码是无效的,则返回(RuneError, 1)。

这两种情况都是不可能的,正确的、非空的UTF-8的结果。

如果一个编码是不正确的UTF-8,编码的符文是编码,但是超出了范围,或者不是最短的UTF-8编码。
那么编码是无效的。不进行其他验证。

func DecodeLastRune(p []byte) (r rune, size int) {...}

使用实例:

    b := []byte("Hello, 世界")

    for len(b) > 0 {
        r, size := utf8.DecodeLastRune(b)
        fmt.Printf("%c %v\n", r, size)

        b = b[:len(b)-size]
    }
    // Output:
    // 界 3
    // 世 3
    //   1
    // , 1
    // o 1
    // l 1
    // l 1
    // e 1
    // H 1

8 对比解码函数DecodeLastRuneInString与DecodeLastRune

它们类似,DecodeLastRuneInString输入是一个字符串。

如果s是空的,则返回(RuneError, 0)。
否则,如果编码是无效的,它返回(RuneError, 1)。

对于正确的非空的UTF-8来说,这两种结果都是不可能的。

如果一个编码是不正确的UTF-8,编码的符文是编码但是超出了范围,或者不是最短的UTF-8编码。
那么编码是无效的。不进行其他验证。

func DecodeLastRuneInString(s string) (r rune, size int) {...}

实例:

    str := "Hello, 世界"
    for len(str) > 0 {
        r, size := utf8.DecodeLastRuneInString(str)
        fmt.Printf("%c %v\n", r, size)
        str = str[:len(str)-size]
    }
    // Output:
    // 界 3
    // 世 3
    //   1
    // , 1
    // o 1
    // l 1
    // l 1
    // e 1
    // H 1

9 RuneLen返回对符文进行编码所需的字节数。

如果符文不是一个可以用UTF-8编码的有效值,则返回-1。

func RuneLen(r rune) int {...}

实例:

    fmt.Println(utf8.RuneLen('a'))
    fmt.Println(utf8.RuneLen('界'))
    // Output:
    // 1
    // 3

10 符文编码函数EncodeRune

将符文的UTF-8编码写进参数p(必须足够大

如果符文超出了范围,它就写出RuneError的编码,它返回写入的字节数。

func EncodeRune(p []byte, r rune) int {...}

实例: 正常使用

    r := '世'
    buf := make([]byte, 3)

    n := utf8.EncodeRune(buf, r)

    fmt.Println(buf)
    fmt.Println(n)
    // Output:
    // [228 184 150]
    // 3

实例:索引越界

    runes := []rune{
        // Less than 0, out of range.
        -1,
        // Greater than 0x10FFFF, out of range.
        0x110000,
        // The Unicode replacement character.
        utf8.RuneError,
    }
    for i, c := range runes {
        buf := make([]byte, 3)
        size := utf8.EncodeRune(buf, c)
        fmt.Printf("%d: %d %[2]s %d\n", i, buf, size)
    }
    // Output:
    // 0: [239 191 189] � 3
    // 1: [239 191 189] � 3
    // 2: [239 191 189] � 3

11 符文扩充函数AppendRune

符文扩充函数将r的UTF-8编码附加到p的末尾,并且返回扩展的缓冲区。

如果符文超出了范围。

它将附加RuneError的编码。

func AppendRune(p []byte, r rune) []byte {...}

实例:

    buf1 := utf8.AppendRune(nil, 0x10000)
    buf2 := utf8.AppendRune([]byte("init"), 0x10000)
    fmt.Println(string(buf1))
    fmt.Println(string(buf2))
    // Output:
    // 𐀀
    // init𐀀

12 符文计量RuneCount

符文计量函数RuneCount返回p中符文的数量。

错误的和短小的编码被视为宽度为1字节的单一符文。

func RuneCount(p []byte) int {...}

实例:
    buf := []byte("Hello, 世界")
    fmt.Println("bytes =", len(buf))
    fmt.Println("runes =", utf8.RuneCount(buf))
    // Output:
    // bytes = 13
    // runes = 9

13 对比符文计量函数RuneCountInString与RuneCount

RuneCountInString输入是一个字符串, 返回符文数量。

func RuneCountInString(s string) (n int) {...}

实例:

    str := "Hello, 世界"
    fmt.Println("bytes =", len(str))
    fmt.Println("runes =", utf8.RuneCountInString(str))
    // Output:
    // bytes = 13
    // runes = 9

14 符文首字符RuneStart

该函数RuneStart,报告该字节是否为编码后的第一个字节,可能是无效的符文。

第二个和随后的字节总是将前两个位设置为10。

func RuneStart(b byte) bool { return b&0xC0 != 0x80 }

实例:

    buf := []byte("a界")
    fmt.Println(utf8.RuneStart(buf[0]))
    fmt.Println(utf8.RuneStart(buf[1]))
    fmt.Println(utf8.RuneStart(buf[2]))
    // Output:
    // true
    // true
    // false

15 合法性检查函数Valid

报告参数p是否完全由有效的UTF-8编码的符文组成。

这种优化避免了在生成p[8:]的代码时重新计算容量的需要。

在生成p[8:]的代码时需要重新计算容量,使其与ValidString相媲美。

ValidString,后者在长ASCII字符串上快20%。

将两个32位的负载结合起来,可以使相同的代码用于32位和64位平台。

编译器可以为first32和second32生成一个32位的负载,在许多平台上。参见test/codegen/memcombine.go。

func Valid(p []byte) bool {...}

实例:

    valid := []byte("Hello, 世界")
    invalid := []byte{0xff, 0xfe, 0xfd}

    fmt.Println(utf8.Valid(valid))
    fmt.Println(utf8.Valid(invalid))
    // Output:
    // true
    // false

16 ValidString

符文字符检查函数ValidString报告s是否完全由有效的UTF-8编码的符文组成。

每次迭代检查并跳过8个字节的ASCII字符。

将两个32位的负载结合起来,可以使相同的代码用于用于32位和64位平台。

编译器可以为first32和second32生成一个32位的负载。

在许多平台上。参见test/codegen/memcombine.go。

func ValidString(s string) bool {...}

实例:

    valid := "Hello, 世界"
    invalid := string([]byte{0xff, 0xfe, 0xfd})

    fmt.Println(utf8.ValidString(valid))
    fmt.Println(utf8.ValidString(invalid))
    // Output:
    // true
    // false

17 编码合法性检查ValidRune

编码合法性检查函数报告r是否可以被合法地编码为UTF-8。

超出范围的代码点或代用半数是非法的。

func ValidRune(r rune) bool {...}

实例:

    valid := 'a'
    invalid := rune(0xfffffff)

    fmt.Println(utf8.ValidRune(valid))
    fmt.Println(utf8.ValidRune(invalid))
    // Output:
    // true
    // false
目录
相关文章
|
3月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
3月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
3月前
|
开发框架 前端开发 Go
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
532 7
|
3月前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
53 3
|
3月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
3月前
|
SQL 安全 Java
阿里双十一背后的Go语言实践:百万QPS网关的设计与实现
解析阿里核心网关如何利用Go协程池、RingBuffer、零拷贝技术支撑亿级流量。 重点分享: ① 如何用gRPC拦截器实现熔断限流; ② Sync.Map在高并发读写中的取舍。
122 0
|
3月前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
52 0
|
3月前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片
|
3月前
|
算法 安全 Go
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
80 2
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
|
3月前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
114 20