zoukankan      html  css  js  c++  java
  • golang -- 字符串就地取反

    字符串

    定义

    在golang中字符串是一种不可变的字节序列,它可以包含任意的数据,包括0值字节,但主要是人类可以阅读的文本。golang中默认字符串被解读为utf-8编码的Unicode码点(文字符号)序列。

    特性

    golang中字符串具有不可变性。例如

    str := "hello 世界!"
    
    str[0] = 'L'
    

    这种写法会引起编译错误:str[0] 不可赋值

    字符串支持类似数组中分片的引用写法:

    
    fmt.Println(str[:5]) // 输出 hello
    
    fmt.Println(str[7:]) // 输出 世界
    
    fmt.Println(str[len(s)+1:) // 宕机 
    

    str[i:j] , 当i、j 越界 (j 、i < 0 或 j、i > len(str) )或 j < i 时会发生宕机。

    str := "Hello"
    
    t := str
    
    str += "world"
    

    这种写法可以通过,虽然str指向了一个新的字符串“Hello world”,但t指向的旧字符串仍然存在。
    不可变意味着两个字符串能够安全的共享同一段底层内存,是的复制任何长度字符串的开销都低廉,类似的字符串s及其子串(s[n:])字符串的复制也开销低廉。

    常见问题

    1. 顺序输出字符串中的每一个字符。

    这个问题乍一看十分的简单,直接遍历就好了:

    
    str := "Hello 世界!" 
    
    for i := 0; i < len(str) ; i++{
        fmt.Printf(“%c 	”,str[i])
    }
    
    

    然而事实没那么简单,其输出结果如下:

    H	e	l	l	o	 	ä	¸	­	å	›	½	ï	¼	
    

    中文字符部分全部为乱码。这与utf-8的编码规则有关, utf-8是以字节为单位对unicode码点进行变长编码。每一个文字符号用14个字节表示,ASCII字符仅仅占1字节的内存,其他常用的文书符号会占到23个字节。一个字符编码的首字节高位指明后面还有多少个字节:

    规则 表示范围 说明
    0xxxxxxx 文字符号0~127 Ascii 字符
    110xxxxx 10xxxxxx 128~2047 少于128个未使用的值
    1110xxxxx 10xxxxxx 10xxxxxx 2048~65535 少于2048个未使用的值
    1110xxxxx 10xxxxxx 10xxxxxx 10xxxxxx 65536~0x10ffffff 其他未使用值

    在上面提到的例子里面Hello子串中的字符为Ascii字符,占用一个字节, 而 世界这两个符号占用的字符为 3 个,所以遍历的时候会出现乱码的情况。

    我们这里换一种写法:

    for i , v := range str {
        fmt.Printf("%d	%c
    ", i , v)
    }
    

    其显示结果如下:

    0	H
    1	e
    2	l
    3	l
    4	o
    5	 
    6	中
    9	国
    12 !
    

    以上代码正常的输出了每一个字符包括中文字符。为什么使用range会成功? 因为range在循环的同时进行了隐式的解码。其中 i 表示该字符在字符串中起始的下角标,v要重点说一下,它表示的是字符对应的unicode 点码,在golang中它有一个专门的变量类型 rune (文字符号) ,它是int32类型的别名。在golang中int占用内存大小取决于操作系统底层和计算机硬件,但int32一定是占用 4 bytes ,rune类型在打印输出的时候使用“%c”。

    有了能遍历输出的函数自然很容易的就可以写出取反函数:

    func Reverse(str string)(res string)  {
    	for _, v := range str {
    		res = string(v) + res
    	}
    
    	return
    }
    

    分析和优化

    我们测试一下这个函数的性能:

    func BenchmarkReverse1(b *testing.B) {
    	tStr := "Hello 中国!"
    	for i := 0; i < b.N; i++{
    		Reverse(tStr)
    	}
    }
    

    作者在winx10/arm64 的操作系统中进行测试,cpu 为core i3,内存为 4g(硬件设施比较老旧了),最后得出的分析结果如下:

    goos: windows
    goarch: amd64
    pkg: project/learn/chapeter2
    BenchmarkReverse1-4   	 2000000	       788 ns/op
    PASS
    
    ROUTINE ======================== project/learn/chapeter2.Reverse1 in D:gopathsrcprojectlearnchapeter2str.go
    
     250ms      2.25s (flat, cum) 94.54% of Total
         .          .     14:
         .          .     15:   res = string(rnStr)
         .          .     16:   return
         .          .     17:}
         .          .     18:
      20ms       20ms     19:func Reverse1(str string)(res string)  {
         .          .     20:
      80ms      110ms     21:   for _, v := range str {
     150ms      2.12s     22:           res = string(v) + res
         .          .     23:           //res = fmt.Sprintf("%c%s", v ,res)
         .          .     24:   }
         .          .     25:
         .          .     26:   return
         .          .     27:}
    

    可以看见最耗时的操作就是res 重新赋值的部分,此时有两种情况:1、res字符串执行 + 操作很费时; 2、进行字符转化的时候费时,我们把代码调整一下:

    func Reverse(str string)(res string)  {
    	for _, v := range str {
                    temp := string(v) 
    		res = temp + res
    	}
    
    	return
    }
    

    性能测试结果如下

    ROUTINE ======================== project/learn/chapeter2.Reverse1 in D:gopathsrcprojectlearnchapeter2str.go
    
     230ms      2.31s (flat, cum) 93.90% of Total
         .          .     14:
         .          .     15:   res = string(rnStr)
         .          .     16:   return
         .          .     17:}
         .          .     18:
      10ms       10ms     19:func Reverse1(str string)(res string)  {
      90ms      210ms     20:   for _, v := range str {
      30ms      200ms     21:           temp := string(v)
      90ms      1.88s     22:           res = temp + res
         .          .     23:
         .          .     24:   }
         .          .     25:
      10ms       10ms     26:   return
         .          .     27:}
         .          .     28:
    

    可见res 执行 + 操作要更费时一些,在执行+操作的过程中,要经历 字符串拷贝、底层字节数组内存重新分配(可能被触发)。
    优化的思路很简单,创建一片‘缓存’,用来存储字符串对应的字节数据,最后再统一转化为字符串。

    func Reverse(str string)(res string)  {
    	i:=0
    	cache := make([]byte, len(str))
    
    	for _, v := range str {
    		i += utf8.RuneLen(v)
    		utf8.EncodeRune(cache[len(str) - i:], v)
    	}
    
    	res = string(cache)
    	return
    }
    

    执行结果如下:

    goos: windows
    goarch: amd64
    pkg: project/learn/chapeter2
    BenchmarkReverse2-4      5000000               253 ns/op
    PASS
    ok      project/learn/chapeter2 1.831s
    
    ROUTINE ======================== project/learn/chapeter2.Reverse2 in D:gopathsrcprojectlearnchapeter2str.go
    
     510ms      1.45s (flat, cum) 90.62% of Total
         .          .     29:
      20ms       20ms     30:func Reverse2(str string)(res string)  {
         .          .               31:   i:=0
      20ms      210ms     32:   cache := make([]byte, len(str))
         .          .     33:
     280ms      460ms     34:   for _, v := range str {
      50ms       90ms     35:           i += utf8.RuneLen(v)
     100ms      250ms     36:         utf8.EncodeRune(cache[len(str) - i:], v)
         .          .     37:   }
         .          .     38:
      20ms      400ms     39:   res = string(cache)
      20ms       20ms     40:   return
         .          .     41:}
    

    优化率接近68%。从以上过程我们可以对golang的字符串类型的变量有一个直观的认识。

  • 相关阅读:
    window 窗口对象http://www.diybl.com/course/1_web/javascript/jsjs/2007104/76401.html
    Web开发中的Drag&Drop完全手册http://www.cnblogs.com/birdshome/archive/2006/07/22/457369.aspx
    [转载]window 的 setTimeout() 與 clearTimeout() 方法
    javascript this详解(转) http://www.cnblogs.com/felix2007/archive/2007/03/21/682670.html
    关闭浏览器弹出对话框事件onbeforeunload http://www.blogjava.net/jennyli/articles/82351.html
    页面跳转http://www.9ba.cn/post/235.html
    POJ 3667 Hotel
    hdu 2511 汉诺塔 X
    UESTC Another LCIS
    Hdu 1576 A/B
  • 原文地址:https://www.cnblogs.com/cnblogs-wangzhipeng/p/10577212.html
Copyright © 2011-2022 走看看