詳解Golang中字符串的使用_第1頁
詳解Golang中字符串的使用_第2頁
詳解Golang中字符串的使用_第3頁
詳解Golang中字符串的使用_第4頁
詳解Golang中字符串的使用_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第詳解Golang中字符串的使用目錄1、字符串編碼2、字符串遍歷3、字符串中的字符數(shù)4、字符串trim5、字符串連接6、字節(jié)切片轉(zhuǎn)字符串

1、字符串編碼

在go中rune是一個unicode編碼點。

我們都知道UTF-8將字符編碼為1-4個字節(jié),比如我們常用的漢字,UTF-8編碼為3個字節(jié)。所以rune也是int32的別名。

typerune=int32

當(dāng)我們打印一個英文字符hello的時候,我們可以得到s的長度為5,因為英文字母代表1個字節(jié):

packagemain

import"fmt"

funcmain(){

s:="hello"

fmt.Println(len(s))//5

但是當(dāng)我們打印嗨的時候,會打印3個字節(jié)。因為使用UTF-8,這個字符會被編碼成3個字節(jié):

packagemain

import"fmt"

funcmain(){

s:="嗨"

fmt.Println(len(s))//3

所以,我們使用len內(nèi)置函數(shù)輸出的并不是字符數(shù),而是字節(jié)數(shù)。

下面看一個有趣的例子,我們都知道漢字符使用3個字節(jié)編碼,分別是0xE5,0x97,0xA8。我們運行下面代碼會得到漢字嗨:

packagemain

import"fmt"

funcmain(){

s:=string([]byte{0xE5,0x97,0xA8})

fmt.Println(s)//嗨

所以我們需要知道:

字符集是一組字符,而編碼描述了如何將字符集轉(zhuǎn)換為二進制在Go中,字符串引用任意字節(jié)的不可變切片Go源碼使用UTF-8編碼。因此,所有字符串文字都是UTF-8字符串。但是因為字符串可以包含任意字節(jié),如果它是從其他地方(不是源碼)獲得的,則不能保證它是基于UTF-8編碼的使用UTF-8,一個Unicode字符可以編碼為1到4個字節(jié)在Go中對字符串使用len返回字節(jié)數(shù),而不是字符數(shù)

2、字符串遍歷

我們在開發(fā)中經(jīng)常會用到對字符串進行遍歷的場景。也許我們想對字符串中的每個rune執(zhí)行一個操作,或者實現(xiàn)一個自定義函數(shù)來搜索特定的子字符串。在這兩種情況下,我們都必須遍歷字符串的不同字符。但往往會得到讓我們意想不到的結(jié)果。

我們看下下面的例子,打印一個字符串中的不同字符和對應(yīng)的位置:

packagemain

import"fmt"

funcmain(){

s:="h嗨llo"

fori:=ranges{

fmt.Printf("字符位置%d:%c\n",i,s[i])

fmt.Printf("len=%d\n",len(s))

輸出結(jié)果:

gorun7.go

字符位置0:h

字符位置1:

字符位置4:l

字符位置5:l

字符位置6:o

len=7

我們想要的效果是通過遍歷字符串,打印出每個字符的索引。但是我們卻得到了一個特殊的字符,其實我們想要的是嗨。

但是打印的字節(jié)數(shù)是符合我們的預(yù)期的,因為嗨是一個中文占用了3個字節(jié),所以len返回的是7。

3、字符串中的字符數(shù)

如果我們想要正確的獲取字符串的字符數(shù),可以使用go中的utf8包:

packagemain

import(

"fmt"

"unicode/utf8"

funcmain(){

s:="h嗨llo"

fori:=ranges{

fmt.Printf("字符位置%d:%c\n",i,s[i])

fmt.Printf("len=%d\n",len(s))

fmt.Printf("runelen=%d\n",utf8.RuneCountInString(s))//獲取字符數(shù)

輸出結(jié)果:

gorun7.go

字符位置0:h

字符位置1:

字符位置4:l

字符位置5:l

字符位置6:o

len=7

runelen=5

在這個例子中,可以看到,我們確實遍歷了5次,也就是對應(yīng)字符串的5個字符。但是我們獲取到的索引其實是對應(yīng)每個字符的起始位置。像下面這樣

那我們?nèi)绾未蛴〕稣_的結(jié)果呢?我們稍微修改下代碼:

packagemain

import(

"fmt"

"unicode/utf8"

funcmain(){

s:="h嗨llo"

fori,v:=ranges{//此處改為獲取v,可以獲取到字符本身

fmt.Printf("字符位置%d:%c\n",i,v)

fmt.Printf("len=%d\n",len(s))

fmt.Printf("runelen=%d\n",utf8.RuneCountInString(s))

輸出結(jié)果:

gorun7.go

字符位置0:h

字符位置1:嗨

字符位置4:l

字符位置5:l

字符位置6:o

len=7

runelen=5

另外一種方法就是把字符串轉(zhuǎn)換成rune切片,這樣也會正確打印結(jié)果:

packagemain

import(

"fmt"

"unicode/utf8"

funcmain(){

s:="h嗨llo"

b:=[]rune(s)

fori:=rangeb{

fmt.Printf("字符位置%d:%c\n",i,b[i])

fmt.Printf("len=%d\n",len(s))

fmt.Printf("runelen=%d\n",utf8.RuneCountInString(s))

輸出結(jié)果:

gorun7.go

字符位置0:h

字符位置1:嗨

字符位置2:l

字符位置3:l

字符位置4:o

len=7

runelen=5

下面是rune切片遍歷的過程(中間省略了將字節(jié)轉(zhuǎn)換為rune的過程,需要遍歷字節(jié),復(fù)雜度為O(n))

4、字符串trim

開發(fā)中我們經(jīng)常會遇到去除字符串頭部或者尾部字符的操作。比如我們現(xiàn)在有個字符串xohelloxo,現(xiàn)在我們想去除尾部的xo,可能我們會像下面這樣寫:

packagemain

import(

"fmt"

"strings"

funcmain(){

s:="xohelloxo"

s=strings.TrimRight(s,"xo")

fmt.Println(s)

輸出結(jié)果:

gorun7.go

xohell

可以看到這不是我們期望的結(jié)果。我們可以看下TrimRight的工作原理:

從右側(cè)取出第一個字符o,判斷是否在xo中,在就移除重復(fù)步驟1,知道不符合條件

所以就可以解釋通了。當(dāng)然和它相似的TrimLeft和Trim也是一樣的原理。

如果我們只想刪除最后xo可以使用TrimSuffix函數(shù):

packagemain

import(

"fmt"

"strings"

funcmain(){

s:="xohelloxo"

s=strings.TrimSuffix(s,"xo")

fmt.Println(s)

輸出結(jié)果:

gorun7.go

xohello

當(dāng)然也有對應(yīng)的從前面刪除的函數(shù)TrimPrefix。

5、字符串連接

開發(fā)中我們經(jīng)常會用到連接字符串的操作,在go中我們一般有2種方式。

我們先看下+號連接的方式:

packagemain

import(

"fmt"

"strings"

funcimplode(values[]string,operatestring)string{

s:=""

for_,value:=rangevalues{

s+=operate

s+=value

s=strings.TrimPrefix(s,operate)

returns

funcmain(){

a:=[]string{"hello","world"}

s:=implode(a,"")

fmt.Println(s)

輸出結(jié)果:

gorun7.go

helloworld

這種方式的缺點就是,由于字符串的不變性,每次+號賦值的時候s不會被更新,而是重新分配內(nèi)存,所以這種方式對性能有很大影響。

還有一種方式就是使用strings.Builder:

packagemain

import(

"fmt"

"strings"

funcimplode(values[]string,operatestring)string{

sb:=strings.Builder{}

for_,value:=rangevalues{

_,_=sb.WriteString(operate)

_,_=sb.WriteString(value)

s:=strings.TrimPrefix(sb.String(),operate)

returns

funcmain(){

a:=[]string{"hello","world"}

s:=implode(a,"")

fmt.Println(s)

輸出結(jié)果:

gorun7.go

helloworld

首先,我們創(chuàng)建了一個strings.Builder結(jié)構(gòu)。在每次遍歷中,我們通過調(diào)用WriteString方法構(gòu)造結(jié)果字符串,該方法將value的內(nèi)容附加到其內(nèi)部緩沖區(qū),從而最大限度地減少內(nèi)存復(fù)制。

WriteString的第二個參數(shù)返回的是error,但是error的值會一直為nil。之所以有第二個error參數(shù)是因為我strings.Builder實現(xiàn)了io.StringWriter接口,它包含一個方法:WriteString(sstring)(nint,errerror)。

我們看下WriteString的內(nèi)部是什么樣的:

func(b*Builder)WriteString(sstring)(int,error){

b.copyCheck()

b.buf=append(b.buf,s...)

returnlen(s),nil

我們可以看到b.buf是一個字節(jié)切片,而里面的實現(xiàn)是使用了append方法。我們知道如果切片很大,使用append會讓底層數(shù)組不斷擴容,影響代碼執(zhí)行效率。

我們知道解決這個問題的方法是,如果事先知道切片的大小,我們可以在初始化的時候就分配好切片的容量。

所以上面的字符串連接還有一種優(yōu)化方案:

packagemain

import(

"fmt"

"strings"

funcimplode(values[]string,operatestring)string{

total:=0

fori:=0;ilen(values);i++{

total+=len(values[i])

total+=len(operate)*len(values)

sb:=strings.Builder{}

sb.Grow(total)//這里會重新分配b.buf的長度和容量

for_,value:=rangevalues{

_,_=sb.WriteString(operate)

_,_=sb.WriteString(value)

s:=strings.TrimPrefix(sb.String(),operate)

returns

funcmain(){

a:=[]string{"hello","world"}

s:=implode(a,"")

fmt.Println(s)

輸出結(jié)果:

gorun7.go

helloworld

6、字節(jié)切片轉(zhuǎn)字符串

需要明確的是,字

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論