本文始發於個人公眾號:TechFlow,原創不易,求個關注
今天是golang專題的第6篇文章,這篇主要和大家聊聊golang當中的字符串的使用。
字符串定義
golang當中的字符串本質是只讀的字符型數組,和C語言當中的char[]類似,但是golang為它封裝了一個變量類型,叫做string。知道了string這個類型之后,我們就可以很方便地來初始化:
var str string
str1 := "hello world" var str2 = "hello world too"
這里應該沒什么難度,很好理解。由於這個數組是只讀的,所以我們可以通過下標獲取某一位的字符,但是不允許修改。
// 允許
fmt.Println(str1[3]) // 錯誤 str1[3] = 'l'
這個也不是golang的獨創,很多語言當中都有這個限制,因為會將字符串作為const類型存儲在專門的區域。所以不允許字符串進行修改,比如Python也是如此。
除了像是數組一樣,支持下標的訪問之外,go中的字符串還支持拼接以及求長度的操作。我們可以用len函數獲取一個字符串的長度,用+來表示字符串的拼接:
len("hello")
// 5 c := "hello" + "world" // c="helloworld"
這些本來也屬於常規操作,並不值得一提,但是關於len函數,值得仔細說說。這里有一個坑,關於utf-8編碼。我們來看下面這個例子:
str := "hello 世界"
fmt.Println(len(str))
按照我們的設想,它返回的應該是8,但是實際上我們這么操作會得到12。原因很簡單,因為在utf-8編碼當中,一個漢字需要3個字節編碼。那如果我們想要得到字符串本身的長度,而不是字符串占據的字節數,應該怎么辦呢?這個時候,我們需要用到一個新的結構叫做rune,它表示單個Unicode字符。
所以我們可以將string轉化成rune數組,之后再來計算長度,得到的結果就准確了。
str := "hello 世界"
fmt.Println(len([]rune(str)))
這樣我們得到的結果就是8了,和我們預期一致了。如果你在使用golang的時候,需要用到utf-8編碼,一定要小心。
類型轉換
golang當中的字符串不像Java或者其他語言一樣封裝地非常完善,當我們想要將整形或者是浮點型轉成字符串,或者是想要將字符串轉成整形和浮點型的時候並沒有方法可以直接調用,而必須要通過庫函數。golang當中提供了strconv庫,用來實現字符串的一些操作。
字符串轉整數、浮點數
字符串轉整數的方法有兩個,一個是ParseInt還有一個是ParseUint,這兩個方法本質上都是將字符串轉成整數。區別在於前者會保留符號,后者是無符號的,用於無符號整數。
這兩個函數都接受三個參數,第一個參數是要轉類型的字符串,第二個參數int的進制,比如二進制、八進制還是16進制、32進制。第三個參數表示返回bit的大小,有效值為0,8,16,32,64,如果傳入0就返回int或者是uint類型,如果是32,則會返回int32類型。
函數的返回值有兩個,第一個是類型轉換之后的結果,第二個是一個error,也就是異常類型,表示在轉換的過程當中是否有出現異常。如果沒有異常,那么這個值會是一個nil。我們判斷異常是否是nil就知道有無錯誤產生,這也是golang當中判斷操作有沒有異常的常規做法。
所以,代碼寫出來會是這樣的:
value, err := strconv.ParseInt("33225", 10, 32)
if err != nil { fmt.Println("error happens") }
如果你不想要這么多功能,就想簡單一點將字符串轉成int來使用,也可以通過Atoi函數。相比於ParseInt它要簡單一些, 只需要傳入字符串即可,它默認按照10進制進行轉換,並且轉換之后會返回int類型的整數。
value, err := strconv.Atoi("33234")
if err != nil { fmt.Println("error happens") }
字符串轉浮點數只有一個函數,就是ParseFloat,由於浮點數沒有進制一說,所以它只有兩個參數。第一個參數是待轉的字符串,第二個參數是bit的大小。和ParseInt一樣,它會返回兩個結果,一個是轉換之后的結果,一個是error異常。
value, err := strconv.ParseFloat("33.33", 32)
if err != nil { fmt.Println("error happens") }
整數、浮點數轉字符串
將整數和浮點數轉字符串都是用Format方法,根據我們要轉的類型不同,分為FormatInt和FormatFloat。FormatInt可以認為是ParseInt的逆向操作,我們固定傳入一個int64的類型,和整數的進制。golang會根據我們的數字和進制,將它轉成我們需要的字符串。
如果指定的進制超過10進制,那么會使用a-z字母來表示大於10的數字。
比如我們把180轉成16進制,會得到b4
num := 180
fmt.Println(strconv.FormatInt(int64(num), 16))
如果我們固定要按照10進制的整數進行轉換,golang還為我們提供了簡化的函數Itoa,默認按照10進制轉化,它等價於FormatInt(i, 10),這樣我們只需要傳入一個值即可。
num := 180
fmt.Println(strconv.Itoa(num))
浮點數轉字符串邏輯大同小異,但是傳參稍有變化。因為浮點數可以用多種方式來表示,比如科學記數法或者是十進制指數法等等。golang當中支持了這些格式,所以允許我們通過傳入參數來指定我們希望得到的字符串的格式。
FormatFloat接受4個參數,第一個參數就是待轉換的浮點數,第二個參數表示我們希望轉換之后得到的格式。一共有'f', 'b', 'e', 'E', 'g', 'G'這幾種格式。
看起來有些眼花繚亂,我們仔細說說。
'f' 表示普通模式:(-ddd.dddd)
'b' 表示指數為二進制:(-ddddp±ddd)
'e' 表示十進制指數,也就是科學記數法的模式:(-d.dddde±dd)
'E' 和'e'一樣,都是科學記數法的模式,只不過字母e大寫:(-d.ddddE±dd)
'g' 表示指數很大時用'e'模式,否則用‘f'模式
'G' 表示指數很大時用’E'模式,否則用'f'模式
我們來看個例子:
num := 23423134.323422
fmt.Println(strconv.FormatFloat(float64(num), 'f', -1, 64)) fmt.Println(strconv.FormatFloat(float64(num), 'b', -1, 64)) fmt.Println(strconv.FormatFloat(float64(num), 'e', -1, 64)) fmt.Println(strconv.FormatFloat(float64(num), 'E', -1, 64)) fmt.Println(strconv.FormatFloat(float64(num), 'g', -1, 64)) fmt.Println(strconv.FormatFloat(float64(num), 'G', -1, 64))
得到的結果如下:

字符串和bool型轉換
除了常用的整數和浮點數之外,strconv還支持與bool類型進行轉換。
其中將字符串轉成bool類型用的是ParseBool,它只有一個參數,只接受0, 1, t, f, T, F, ture, false, True, False, TRUE, FALSE這幾種取值,否則會返回錯誤。
flag, err := strconv.ParseBool('t')
if err != nil { fmt.Println("error happens") }
將bool轉字符串調用FormatBool方法,它也只有一個參數,就是一個bool類型的變量,返回值也是確定的,如果是True就返回"true", 如果是False就返回"false"。
fmt.Println(strconv.FormatBool(true))
字符串運算包
前面介紹的strconv包是golang當中字符串的一個轉換操作包,可以用來將字符串轉成其他類型,將其他類型轉化成字符串。關於字符串本身的一些操作,還有一個專門的包叫做strings。
字符串比較
我們可以通過strings.Compare來比較兩個字符串的大小,這個函數類似於C語言當中的strcmp,會返回一個int。
cmp := strings.Compare(str1, str2)
cmp等於-1表示str1字典序小於str2,如果str1和str2相等,cmp等於0。如果cmp=1,表示str1字典序大於str2.
查找函數
我們可以用Index函數查找一個字符串中子串的位置,它會返回第一次出現的位置,如果不存在返回-1.
var theInd = strings.Index(str, "sub")
類似的方法是LastIndex,它返回的是出現的最后一個位置,同樣,如果不存在返回-1.
var theLastIdx = strings.LastIndex(str, "last")
Count和Repeat
我們可以用Count來統計子串在整體當中出現的次數。
strings.Count("abcabcabababc", "abc")
第一個參數是母串,第二個參數是子串。如果子串為空,則返回母串的長度+1.
有count自然就有重復,我們可以用Repeat方法來講字符串重復指定的次數:
repeat := strings.Repeat("abc", 10)
Replace、Split和Join
還有Replace函數,可以替換字符串中的部分。這個函數接收四個參數,分別是字符串,匹配串和目標串,還有替換的次數。如果小於0,表示全部替換。
str := "aaaddc"
strings.Replace(str, "a", "b", 1) // baaddc strings.Replace(str, "a", "b", -1)
我們還可以通過Split方法來分割字符串,它的使用方法和Python當中的split一樣,我們傳入字符串與分隔符,會返回根據分隔符分割之后的字符串數組:
str := "abc,bbc,bbd"
slice := strings.Split(str, ",")
除了Split之外,我們也經常使用它的逆操作也就是Join。通過我們指定的分隔符,將一個字符串數組拼接在一起。
slice := []string{"aab", "aba", "baa"}
str := strings.Join(slice, ",")
strings當中的函數除了剛才列舉的之外還有很多,比如用來去除字符串首尾多余字符的Trim和TrimLeft,判斷是否包含前綴的HasPrefix和判斷是否包含后綴的HasSufix等等,由於篇幅限制,不一一列舉了,大家用到的時候可以查閱strings的api文檔。
總結
到這里,關於golang當中string的一些基本用法就介紹完了。一般來說,我們日常需要用到的功能,strings和strconv這兩個庫就足夠使用了。初學者可能經常會把這兩個庫搞混淆,其實很容易分清,strings當中封裝的是操作字符串的一些函數。比如字符串判斷、join、split等各種處理,而strconv是專門用來字符串和其他類型進行轉換的,除此之外基本上沒有其他的功能。牢記這兩點之后,很容易區分開。
今天介紹的api有些多,如果記不過來也沒有關系, 我們只需要大概有一個印象即可,具體可以使用到的時候再去查閱相關的資料。
如果覺得有所收獲,請給我一個關注。