• Go中rune类型浅析


    一、字符串简单遍历操作

    在很多语言中,字符串都是不可变类型,golang也是。

    1、访问字符串字符

    如下代码,可以实现访问字符串的单个字符和单个字节

    package main
    
    import (
    	"fmt"
    )
    
    // 字符串每个字节十六进制打印
    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])
    	}
    	fmt.Printf("\n")
    }
    
    
    func main(){
    	var s1 = "golang"
    
    	fmt.Printf("s1: %s, len(s1)=%d\n", s1, len(s1))
    	printBytes(s1)
    	printChars(s1)
    }
    

    执行结果为:

    s1: golang, len(s1)=6
    Bytes: 67 6f 6c 61 6e 67
    Charaters: g o l a n g
    

    字符串golang长度为6,%x按照十六进制格式输出每一个字符的ASCII码,%c对应格式输出每一个字符。

    2、查看中文字符存在问题

    上述代码针对字符串s1=golang没有异常,看起来实现的两个函数也是访问字符串单个字符的合法方式,但这有一个严重的错误。上面的程序没有考虑字符串中一个字符占多字节的情况,例如 中文字符。来看下面代码:

    package main
    
    import (
    	"fmt"
    )
    
    // 字符串每个字节十六进制打印
    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])
    	}
    	fmt.Printf("\n")
    }
    
    
    func main(){
    	var s2 = "Go编程"
    
    	fmt.Printf("s2: %s, len(s1)=%d\n", s2, len(s2))
    	printBytes(s2)
    	printChars(s2)
    
    }
    

    执行结果为:

    s2: Go编程, len(s1)=8
    Bytes: 47 6f e7 bc 96 e7 a8 8b
    Charaters: G o ç ¼ * _ ^ ~
    

    按照想法,应该正常打印出Charaters: G o 编 程的字符,但是输出Charaters: G o ç ¼ *乱码,而明显的长度应该为4,这里显示len(s1)=8,长度为8。

    3、原因

    golang中string底层是通过byte数组实现的,所以直接求len,实际是在按字节长度计算。中文字符在unicode下占2个字节,在utf-8编码下占3个字节,而golang默认编码正好是utf-8。所以一个汉字占3个字节算了3个长度。所以打印长度为8=2(Go)+3(编)+3(程)。

    二、rune类型

    1、使用rune类型遍历字符

    如何按照预期一个包含中文字符的字符串长度,而非字节长度?代码如下:

    
    package main
    
    import (
        "fmt"
        "unicode/utf8"
    )
    
    
    func printChars(s string) { 
        fmt.Printf("Characters: ") 
        runes := []rune(s) 
        for i := 0; i < len(runes); i++ {               
          fmt.Printf("%c ", runes[i]) 
        } 
    }
    
    func main() {
    
        var s = "Go编程"
        fmt.Println("s :", s)
        // 输出字符占用字节个数
        fmt.Println("len(s):", len(s))
        
        // 方案一
        //golang中的unicode/utf8包提供了用utf-8获取长度的方法
        fmt.Println("RuneCountInString:", utf8.RuneCountInString(str))
    
        //方案二
        //通过rune类型处理unicode字符
        fmt.Println("rune:", len([]rune(str)))
        
        printBytes(s)
    }
    

    执行结果:

    s: Go编程
    len(s): 8
    RuneCountInString: 4
    rune: 4
    Characters: Go编程
    

    可以通过rune类型转换可以正常输出每个中文字符,包括字符串中的字符个数。

    说明:golang中rune是内置类型,是int32类型的别名,而byte数据类型等同于int8类型

    • byte常用来处理ascii字符
    • rune可以处理utf-8字符

    2、 获取字节下标

    为了更进一步看清楚每个字符占用的字节数,查看字符串中字符的字节下标

    package main
    
    import (
    	"fmt"
    )
    
    func charsIndex(s string) {
    	for index, r := range s {
    		fmt.Printf("字符 %c 从第 %d 个字节开始\n", r, index)
    	}
    }
    
    func main() {
    	s := "Go编程"
    	fmt.Println("s: ", s)
    	fmt.Println("len rune:", len([]rune(s)))
    	charsIndex(s)
    }
    

    执行结果如下

    s:  Go编程
    len rune: 4
    字符 G 从第 0 个字节开始
    字符 o 从第 1 个字节开始
    字符 编 从第 2 个字节开始
    字符 程 从第 5 个字节开始
    

    从上面输出可以看到,在utf-8编码下,每个英文字符占用1个字节,中文字符占用3个字节。

  • 相关阅读:
    LeetCode——Path Sum II
    注解配置 ssh
    冒泡排序深入具体解释
    在web page中使鼠标右击失效的几种方法
    XMPP个人信息展示
    android一个弹出菜单的动画(二)
    MySQL协议分析
    MySQL DBA教程:Mysql性能优化之缓存参数优化
    INSERT DELAYED 句法
    UDP包的最大大小是多少?
  • 原文地址:https://www.cnblogs.com/welan/p/16318454.html
Copyright © 2020-2023  润新知