這篇文章已經放到騰訊智能工作臺的知識庫啦,鏈接在這里:ima.copilot-Go 入門到入土。要是你有啥不懂的地方,就去知識庫找 AI 聊一聊吧。
本篇將詳細講解 Go 語言中與字符串相關的操作。
1、rune 和 字符串長度
1、Go 函數語法約定
在開始之前,需要注意 Go 語言的一個語法規范。函數或流程控制語句的左花括號 {
必須與函數名或關鍵字在同一行,不能換行書寫。
這種強制性規定統一了代碼風格,避免了不同開發者因個人習慣造成代碼格式的混亂。
2、len()
計算字符串長度
Go 語言提供了一個內置函數 len()
,可以方便地計算出字符串的長度。
在上述代碼中,len()
函數直接返回了字符串 name
的長度。這是一個非常便捷和常見的操作。
3、len()
的本質:字節數
len()
函數雖然通用,但它返回的是字符串所占的字節數(the number of bytes),而不是字符數。
當字符串只包含英文字母、數字或標準 ASCII 符號時,一個字符恰好占用一個字節,此時字節數等于字符數。但當字符串中包含非 ASCII 字符(如中文、日文等)時,情況就有所不同。
在 UTF-8 編碼(Go 語言默認的字符串編碼)中:
-
一個英文字符占用 1 個字節。
-
一個中文字符通常占用 3 個字節。
直觀上看,字符串 “叫我阿杰好l” 包含 6 個字符。但 len()
的計算結果為什么是 16 呢? 因為計算的是字節數:1 (l) + 5*3 (叫我阿杰好) = 16
個字節。
4、如何獲取真實的字符數?
在很多業務場景中,我們關心的是字符串實際包含多少個字符,而不是它占用了多少字節。為了解決這個問題,我們需要將字符串轉換為 rune
切片。
rune
類型是 Go 語言中用于表示單個 Unicode 字符的特殊類型。將字符串轉換為 []rune
后,每個中文字符和英文字符都會被當作一個獨立的元素。此時再使用 len()
函數,就能得到準確的字符數量。
5、總結
在 Go 語言中計算字符串長度時,必須注意以下關鍵點:
-
如果你的字符串只包含英文或 ASCII 字符,或者你關心的就是字節長度,可以直接使用
len(str)
。 -
如果你需要知道字符串中實際的字符數量(特別是處理包含中文等多字節字符的場景),必須先將字符串轉換為
rune
切片[]rune(str)
,然后再對其使用len()
函數。
掌握這個細節對于正確處理多語言文本至關重要。
2、轉義符詳解
1、 為什么需要轉義符?
假設我們需要定義一個字符串,其內容本身就包含特殊字符,例如雙引號。
如果我們直接這樣編寫代碼:
編譯器會報錯。因為在 Go 語言中,雙引號用于界定字符串的開始和結束。上述代碼會被編譯器誤解為:第一個字符串是 "Go "
,第二個字符串是 ""
,而中間的 體系課
則是不合法的語法。
為了在字符串內部正確地包含雙引號等特殊字符,我們就需要使用轉義符。
2、使用反斜杠 \
進行轉義
在 Go 中,反斜杠 \
是核心的轉義符。當它出現在一個特殊字符前時,它會“轉義”該字符,使其失去原有的特殊含義,而被當作一個普通的字符來處理。
示例:
要在一個由雙引號定義的字符串中包含雙引號,我們可以在內部的雙引號前加上 \
:
在這里,\"
被編譯器視為一個整體,并被解釋為單個雙引號字符。
2、使用反引號 `
創建原生字符串
除了使用轉義符,Go 語言還提供了一種更簡潔的方式來處理包含大量特殊字符的字符串:原生字符串字面量 (Raw String Literal)。這種字符串使用反引號 `
(通常位于鍵盤 Tab 鍵的上方) 來定義。
在反引號包裹的字符串中,所有的字符都按其字面意義進行解釋,無需任何轉義。
這種寫法的輸出結果與使用轉義符完全相同。反引號內的內容可以隨意編寫,包括換行符和雙引號,這與 Python 中的三引號字符串有些類似,非常適合定義多行文本或包含復雜特殊字符的文本。
3、常見的轉義序列
無論是否使用原生字符串,理解并掌握常見的轉義序列都至關重要。轉義符 \
可以與多個字符組合,形成具有特殊含義的序列。這些序列通常與 ASCII 控制碼相對應。
以下是一些最常用的轉義序列:
|序列|含義|描述|
|—|—|—|
|\n
|換行符|將光標移動到下一行的開頭 (Line Feed)|
|\r
|回車符|將光標移動到當前行的開頭 (Carriage Return)|
|\t
|水平制表符|相當于按下 Tab 鍵,用于對齊文本|
|\\
|反斜杠|在字符串中插入一個反斜杠字符 \
|
|\"
|雙引號|在字符串中插入一個雙引號字符 "
|
|\'
|單引號|在字符串中插入一個單引號字符 '
|
|\?
|問號|在字符串中插入一個問號字符 ?
|
其中,\r\n
組合(回車并換行)在 Windows 系統中常被用作標準的換行符,而 \n
在 Unix/Linux 和 macOS 中更為常見。
4、fmt.Print
與 fmt.Println
的區別
在 Go 中,標準庫 fmt
提供了多種打印函數,其中 Print
和 Println
的一個關鍵區別在于是否自動換行。
-
fmt.Println
:在打印完所有參數后,會自動在末尾添加一個換行符。 -
fmt.Print
:僅打印傳入的參數,不會在末尾添加任何內容。
如果我們想用 Print
實現與 Println
相同的換行效果,就需要手動添加轉義符 \n
或 \r\n
。
掌握轉義符是 Go 語言編程的基礎。無論是為了在字符串中嵌入特殊字符,還是為了控制輸出格式,理解 \
和 `
的用法都至關重要。在后續的開發中,我們會頻繁地遇到這些概念,熟練運用它們將極大提升編碼效率和代碼的可讀性。
3、格式化輸出
在實際開發中,我們常常需要將不同類型的變量(如字符串、整數、浮點數等)組合成一個完整的字符串進行輸出。
1、字符串拼接的挑戰
使用簡單的 +
號拼接字符串,在處理復雜或多類型數據時會變得非常繁瑣且難以維護。
可以看到,這種方式不僅代碼可讀性差,還需要手動進行類型轉換(如 strconv.Itoa
),非常不便。
2、使用 fmt.Printf
進行格式化輸出
為了解決上述問題,Go 語言提供了 fmt.Printf
函數。它允許我們使用一個模板字符串和一系列占位符(也稱格式化動詞),將變量優雅地嵌入到字符串中。
Printf
的工作方式:
-
定義一個包含占位符的模板字符串。
-
在字符串后面,按順序提供與占位符對應的變量。
-
函數會自動將變量替換到相應的位置,并輸出到控制臺。
示例:
這段代碼不僅更易讀、更易維護,而且 Go 會自動處理類型,我們無需再關心 int
到 string
的轉換。注意 Printf
不會自動換行,因此我們在模板末尾手動添加了 \n
。
3、生成格式化字符串:fmt.Sprintf
有時我們需要的不是直接打印到控制臺,而是將格式化后的結果保存為一個字符串變量。這時可以使用 fmt.Sprintf
。
它的用法與 Printf
完全相同,唯一的區別是它會返回一個格式化好的字符串,而不是將其打印出來。
這個函數在構建日志信息、生成 API 響應或任何需要先處理再輸出的場景中非常有用。
4、性能說明
通常情況下,fmt.Printf
和 fmt.Sprintf
是構建字符串的首選方法,因為它們極大地提升了代碼的可讀性和可維護性。但在對性能有極端要求的場景中,手動的字符串拼接(如使用 strings.Builder
)可能會有更好的性能表現。對于絕大多數應用,可讀性帶來的好處遠超微小的性能差異。
5、常用格式化占位符
Go 提供了豐富的占位符來控制輸出格式。以下是一些最常用的占位符:
| 占位符 | 描述 | 常用類型 |
| ---------- | -------------------------------- | --------------- |
| 通用 | | |
| %v
| 默認格式的值。對于不同類型,會以最合適的方式展示。 | 任何類型 |
| %+v
| 在 %v
的基礎上,打印結構體時會額外輸出字段名。 | struct
|
| %#v
| Go 語法表示的值。輸出結果是符合 Go 語法的字面量。 | 任何類型 |
| %T
| 值的類型。輸出變量的類型信息。 | 任何類型 |
| 字符串 | | string
|
| %s
| 普通的字符串或 []byte
| string
|
| %q
| 為字符串加上雙引號,并對特殊字符進行安全轉義。 | string
|
| 整數 | | int
, uint
等 |
| %d
| 十進制表示 | int
|
| %b
| 二進制表示 | int
|
| %o
| 八進制表示 | int
|
| %x
, %X
| 十六進制表示 (分別為小寫 a-f
和大寫 A-F
) | int
|
| 浮點數 | | float
|
| %f
| 標準小數表示 (例如 123.456
) | float
|
| %e
, %E
| 科學計數法表示 (例如 1.23456e+02
) | float
|
| 布爾值 | | bool
|
| %t
| true
或 false
| bool
|
| 指針 | | 指針類型 |
| %p
| 指針的十六進制表示(以 0x
開頭) | 指針 |
寬度與對齊示例:
你還可以控制輸出的寬度和對齊方式。
掌握格式化輸出是 Go 語言開發者的必備技能。fmt.Printf
和 fmt.Sprintf
提供了一種強大而靈活的方式來構建和展示字符串,使得代碼更加簡潔和專業。在日常開發中,應優先選擇這些函數來處理字符串格式化任務。
4、高性能字符串拼接
當需要拼接大量字符串,尤其是在循環中,性能就成為一個重要的考量因素。此時,strings.Builder
是最佳選擇。它的性能遠高于 +
拼接和 fmt.Sprintf
。
strings.Builder
通過維護一個內部的字節緩沖區(buffer)來避免每次拼接都重新分配內存,從而實現高效的字符串構建。
使用步驟:
-
創建一個
strings.Builder
實例。 -
使用
WriteString()
或Write()
等方法向其內部緩沖區追加內容。 -
最后調用
String()
方法獲取最終拼接完成的字符串。
雖然 strings.Builder
的代碼看起來比 Sprintf
繁瑣一些,但在性能敏感的場景下,它是構建字符串的不二之選。
如何選擇拼接方式
掌握不同場景下最合適的字符串拼接方法,是編寫高效、可維護 Go 代碼的關鍵。
-
+
拼接:僅適用于少量、簡單的字符串連接。 -
fmt.Sprintf
:日常開發首選。代碼可讀性最高,使用最方便,足以應對絕大多數場景。 -
strings.Builder
:性能要求高的場景首選。當程序中有大量或頻繁的字符串拼接操作(例如在循環內部)時,應優先使用Builder
以獲得最佳性能。
5、字符串的比較
在 Go 中,比較字符串是一項直接且常用的操作。
1、等于 (==
) 和不等于 (!=
)
你可以使用標準的比較運算符 ==
和 !=
來判斷兩個字符串是否完全相同。
2、大于 (>
) 和小于 (<
)
Go 語言同樣支持使用 >
、<
、>=
和 <=
來比較字符串的大小。
比較規則: 字符串的比較是按字典順序進行的。它會從左到右逐個比較兩個字符串中對應位置字符的 ASCII 碼值(或更準確地說是 Unicode 碼點)。一旦發現差異,比較立即結束并返回結果。
6、字符串操作常用方法
Go 的標準庫 strings
提供了大量實用、高效的字符串處理函數。要使用它們,首先需要導入該包:
import ("strings")
下面我們介紹一些最常用的函數。
1、包含與計數
-
strings.Contains(s, substr)
: 判斷字符串s
中是否包含子串substr
。 -
strings.Count(s, substr)
: 計算子串substr
在字符串s
中出現的次數。
2、分割與連接
strings.Split(s, sep)
: 使用分隔符sep
將字符串s
分割成一個字符串切片。
如果分隔符不存在,Split
會返回一個只包含原字符串的切片。
3、前綴與后綴
-
strings.HasPrefix(s, prefix)
: 判斷字符串s
是否以prefix
開頭。 -
strings.HasSuffix(s, suffix)
: 判斷字符串s
是否以suffix
結尾。
4、查找位置
strings.Index(s, substr)
: 查找子串substr
在字符串s
中首次出現的位置(字節索引)。如果不存在,則返回 -1。
注意:Index
返回的是字節位置。對于包含多字節字符(如中文)的字符串,這個位置可能不等于字符的個數。
5、替換
-
strings.Replace(s, old, new, n)
: 將字符串s
中的old
子串替換為new
。參數n
控制替換次數: -
n < 0
(通常用-1
): 全部替換。 -
n > 0
: 最多替換n
次。
6、大小寫轉換
-
strings.ToLower(s)
: 將字符串轉換為全小寫。 -
strings.ToUpper(s)
: 將字符串轉換為全大寫。
7、修剪字符
-
strings.Trim(s, cutset)
: 從字符串s
的兩端移除cutset
中包含的任意字符。 -
strings.TrimSpace(s)
: 移除字符串兩端的空白字符(空格、制表符、換行符等),這是最常用的修剪函數。 -
strings.TrimLeft(s, cutset)
和strings.TrimRight(s, cutset)
: 分別只從左邊或右邊修剪。