1. 程式人生 > >Golang 入門 : 字符串

Golang 入門 : 字符串

str 引號 轉換成 lastindex 方便 說明 div mri pre

在 Golang 中,字符串是一種基本類型,這一點和 C 語言不同。C 語言沒有原生的字符串類型,而是使用字符數組來表示字符串,並以字符指針來傳遞字符串。Golang 中的字符串是一個不可改變的 UTF-8 字符序列,一個 ASCII 碼占用 1個字節,其它字符根據需要占用 2-4 個字節,這一點與其它主流的開發語言( C++、Java、Python)是不同的。這樣設計的好處有兩個:

  • 減少內存的使用,節約硬盤空間
  • 統一編碼格式(UTF-8)有助於減少讀取文件時的編碼和解碼工作

字符串的聲明與初始化

聲明和初始化字符串非常容易:

s := "hello world"

上面的代碼聲明了字符串變量 s,其內容為 "hello world"。在 Golang 中字符串的值是不可變的,當創建一個字符串後,無法再次修改這個字符串的內容。所以如果你通過下面的代碼修改 s 中的內容就會發生編譯錯誤:

s := "hello nick"

字符串字面量

Golang 支持兩種類型的字符串字面量:

  • 解釋型字符串
  • 非解釋型字符串

所謂的解釋型字符串就是用雙引號括起來的字符串(""),其中的轉義字符會被替換掉,這些轉義字符包括:

\a    // 響鈴     
\b    // 退格
\f    // 換頁
\n    // 換行
\r    // 回車
\t    // 制表符
\u    // Unicode 字符
\v    // 垂直制表符
\"    // 雙引號
\\    // 反斜杠

非解釋型字符串是指用反引號( ` 一般在 Esc 鍵下面,數字鍵 1 的左邊)括起來的字符串。在非解釋型字符串中的轉義字符不會被解釋,並且還支持換行。

看下面的 demo:

package main
import "fmt"

func main() {
    s1 := "Hello\nWorld!"
    s2 := `Hello\n
           nick!`
    fmt.Println(s1)
    fmt.Println(s2)
}

運行上面的代碼,輸出如下:

技術分享圖片

反單引號可以跨行,並且引號內的所有內容都會直接輸出,包括轉義字符和空格縮進等。而雙引號則不能換行,並且會解析轉義字符。

字符串的長度

內置函數 len() 可以返回一個字符串中的字節數(註意,不是 rune 字符數目),索引操作 s[i] 可以返回字符串 s 中第 i 個字節的值。

s := "abc你"
fmt.Printf("字符串的字節長度是:%d\n", len(s))
for i := 0; i < len(s); i++ {
    fmt.Println(s[i])
}

字符串的字節長度是:6

97         // a
98         // b
99         // c
228        //
189        //
160        //

最後的三個字節組成了漢字 "你"。

如果要獲取字符串中字符的個數,可以先把字符串轉換成 []rune 類型,然後用 len() 函數獲取字符個數:

s := "abc你"
r := []rune(s)
fmt.Print(len(r))

這次輸出的結果為:4。

從字符串中截取內容

可以通過下面的語法截取字符串中的內容:

s := "abcdef"
s1 := s[1:4]

此時 s1 的內容為 "bcd",該語法通過下標索引的方式截取字符串中的內容,特點是 "左含右不含"。也就是說新的子串包含源串索引為 1 的字符,而不包含源串索引為 4 的字符。
如果要從源串的開始處截取可以省略第一個索引:

s2 := s[:4]

s2 的內容為 "abcd"。
如果要從源串的某個位置開始一直截取到末尾,可以省略第二個索引:

s3 := s[2:]

s3 的內容為 "cdef"。

訪問越界問題
在通過索引訪問字符串或者是截取子串時需要考慮索引越界的問題,如果試圖訪問超出字符串索引範圍的字節將會在運行時導致 panic 異常:

s4 := s[2:10]

技術分享圖片

連接字符串

使用 + 號可輕松的把字符串連接起來:

s := "hello"
s1 := " "
s2 := "world"
s3 := s + s1 + s2

此時 s3 的內容為 "hello world"。

遍歷字符串

由於可以通過下標索引字符串中的字節,所以可以用這種方式遍歷字符串:

s := "abc你好"
for i := 0; i < len(s); i++ {
    fmt.Printf("%c", s[i])
}

輸出的結果如下:
abcä½ å¥½
可見在字符串中含有非單字節的字符時這種方法是不正確的。range 函數能解決這個問題:

for _, v := range s {
    fmt.Printf("%c", v)
}

這次輸出的結果為:
abc你好

修改字符串

在 Golang 中,不能修改字符串的內容,也就是說不能通過 s[i] 這種方式修改字符串中的字符。要修改字符串的內容,可以先將字符串的內容復制到一個可寫的變量中,一般是 []byte 或 []rune 類型的變量,然後再進行修改。
如果要對字符串中的字節進行修改,就轉換為 []byte 類型,如果要對字符串中的字符修改,就轉換為 []rune 類型,在轉換類型的過程中會自動復制數據。

修改字符串中的字節(用 []byte)
對於那些單字節字符來說,可以通過這種方式進行修改:

s := "Hello 世界"
b := []byte(s)    // 轉換為 []byte,數據被自動復制
b[5] = ,        // 把空格改為半角逗號
fmt.Printf("%s\n", s)
fmt.Printf("%s\n", b)

輸出結果為:
Hello 世界
Hello,世界

修改字符串中的字符(用 []rune)

s := "Hello 世界"
b := []rune(s)    // 轉換為 []rune,數據被自動復制
b[6] = 
b[7] = 
fmt.Println(s)
fmt.Println(string(b))

輸出結果為:
Hello 世界
Hello 中國

註意:和 C/C++ 不一樣,Golang 語言中的字符串是根據長度限定的,而非特殊的字符 \0。string 類型的 0 值是長度為 0 的字符串,即空字符串 ""。

strings 包

strings 是非常重要的一種基本類型,所需要執行的操作繁多且比較復雜,因此一般的編程語言都會額外封裝一些方法用於處理字符串。Golang 語言的標準庫中也存在這樣一個名稱為 strings 的庫。下面介紹一些 strings 庫的常見用法。

檢查是否包含子串
判斷一個字符串中是否包含某個子串是經常遇到的一種字符串操作,在 strings 包中,可以使用 Contains() 函數進行判斷:

s := "A good tree bears good fruit"
fmt.Printf("%t\n", strings.Contains(s, "tree"))

輸出的結果為:true。

如果要檢查字符串是不是以某個子串開始的,可以使用 HasPrefix() 函數:

s := "A good tree bears good fruit"
fmt.Printf("%t\n", strings.HasPrefix(s, "A good"))

輸出的結果為:true。

如果要檢查字符串是不是以某個子串結束的,可以使用 HasSuffix() 函數:

s := "A good tree bears good fruit"
fmt.Printf("%t\n", strings.HasSuffix(s, "good fruit"))

輸出的結果為:true。

與 Contains() 函數相比,ContainsAny() 函數能夠匹配更廣泛的內容,並且可以匹配 Unicode 字符:

fmt.Println(strings.Contains("failure", "a & o"))                // false
fmt.Println(strings.Contains("foo", ""))                         // true
fmt.Println(strings.Contains("", ""))                            // true

fmt.Println(strings.ContainsAny("failure", "a & o"))             // true
fmt.Println(strings.ContainsAny("foo", ""))                      // false
fmt.Println(strings.ContainsAny("", ""))                         // false

fmt.Println(strings.ContainsAny("好樹結好果", "好樹"))             // true

獲取子串的索引
在 Golang 中,字符串中的字符都有一個索引值,很多時候我們要操作字符串,就必須先獲取字符在字符串中的索引值。在 strings 包中 Index 函數可以返回指定字符或字符串的第一個字符的索引值,如果不存在則返回 -1:

fmt.Println(strings.Index("Hi I‘m Nick, Hi", "Nick"))                // 7
fmt.Println(strings.Index("Hi I‘m Nick, Hi", "Hi"))                  // 0
fmt.Println(strings.Index("Hi I‘m Nick, Hi", "abc"))                 // -1
fmt.Println(strings.LastIndex("Hi I‘m Nick, Hi", "Hi"))              // 13

LastIndex 函數返回匹配到的最後一個子串的索引值。
如果處理包含多個字節組成的字符的字符串,需要使用 IndexRune 函數來對字符進行定位:

fmt.Println(strings.IndexRune("好樹結好果", ))     // 3

註意這裏返回的是 3,這是 "樹" 的第一個字節在字符串中的位置。

替換字符串
替換字符串最常用的方式其實是通過正則匹配去替換的,其靈活度更高。而 Golang 則為比較基礎的替換操作提供了 Replace 函數:

fmt.Println(strings.Replace("你好世界", "世界", "地球", 1))

輸出的結果為:你好地球
strings.Replate(str, old, new, n) 函數一共有 4 個參數,第一個為源字符串,第二個表示源字符串中需要被替換掉的字符串,第三個是替換的內容,最後一個 n 則表示替換匹配到的前 n 個記錄。

大小寫轉換
操作字符串就免不了大小寫轉換,ToLower() 函數把字符串轉換為小寫,ToUpper() 函數把字符串轉換為大寫:

s := "A good tree bears good fruit"
s1 := "HOW ARE YOU?"
fmt.Printf("%s\n", strings.ToUpper(s))
fmt.Printf("%s\n", strings.ToLower(s1))

輸出的結果如下:
A GOOD TREE BEARS GOOD FRUIT
how are you?

修剪
在處理用戶的輸入時,去掉字符串前後多余的空白字符非常重要,strings 包中提供了 Trem()、TrimLeft() 和 TrimRight() 來實現這個功能:

fmt.Printf("%q\n", strings.Trim(" Golang ", " "))
fmt.Printf("%q\n", strings.TrimLeft(" Golang ", " "))
fmt.Printf("%q\n", strings.TrimRight(" Golang ", " "))

輸出的結果如下:
"Golang"
"Golang "
" Golang"

分隔與拼接
Split() 函數按照指定的分隔符分隔字符串並返回一個切片:

fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a boy a girl a cat", "a "))
fmt.Printf("%q\n", strings.Split("xyz", ""))

輸出的結果如下:
["a" "b" "c"]
["" "boy " "girl " "cat"]
["x" "y" "z"]

Join() 函數則會將元素類型為 string 的切片使用分隔符拼接組成一個字符串:

fmt.Printf("%q\n", strings.Join([]string{"boy", "girl", "cat"}, ";"))

輸出的結果如下:
"boy;girl;cat"

strconv 包

這個包主要用於字符串與其他類型的轉換。這裏我們簡單的看下如何通過 Itoa() 函數把字符串轉換為整型:

num, _ := strconv.Atoi("123")
num += 5
fmt.Printf("%d\n", num)

上面這段代碼輸出的結果為:128
這說明字符串 "123" 被成功的轉換成了十進制整數 123,隨後還進行了加法運算。

總結

對於任何一門編程語言來說,字符串的定義和相關操作都是非常基礎的內容。從本文我們可以看到,Golang 的字符串類型原生支持 Unicode,操作起來也非常的方便,特別是提供了便利的 strings 包。這讓我們能夠輕松的了解並使用 Golang 的 string 類型。

參考:
《Go語言編程入門與實戰技巧》

Golang 入門 : 字符串