##10.1 字符串處理 字符串在開發中常常用到,包括用戶的輸入,數據庫讀取的數據等,咱們常常須要對字符串進行分割、鏈接、轉換等操做,咱們能夠經過Go標準庫中的strings和strconv兩個包中的函數進行相應的操做。正則表達式
###10.1.1 字符串操做 下面這些函數來自於strings包,這裏介紹一些我日常常常用到的函數,更詳細的請參考官方的文檔。數據庫
####10.1.1.1 Contains func Contains(s, substr string) bool 功能:字符串s中是否包含substr,返回bool值編程
示例代碼:json
fmt.Println(strings.Contains("seafood", "foo")) fmt.Println(strings.Contains("seafood", "bar")) fmt.Println(strings.Contains("seafood", "")) fmt.Println(strings.Contains("", "")) //運行結果: //true //false //true //trueapi
####10.1.1.2 Join func Join(a []string, sep string) string 功能:字符串連接,把slice a經過sep連接起來數組
示例代碼:bash
s := []string{"foo", "bar", "baz"} fmt.Println(strings.Join(s, ", ")) //運行結果:foo, bar, baz數據結構
####10.1.1.3 Index func Index(s, sep string) int 功能:在字符串s中查找sep所在的位置,返回位置值,找不到返回-1編程語言
示例代碼:函數
fmt.Println(strings.Index("chicken", "ken")) fmt.Println(strings.Index("chicken", "dmr")) //運行結果: // 4 // -1
####10.1.1.4 Repeat func Repeat(s string, count int) string 功能:重複s字符串count次,最後返回重複的字符串 示例代碼:
fmt.Println("ba" + strings.Repeat("na", 2)) //運行結果:banana
####10.1.1.5 Replace func Replace(s, old, new string, n int) string 功能:在s字符串中,把old字符串替換爲new字符串,n表示替換的次數,小於0表示所有替換
示例代碼:
fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2)) fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1)) //運行結果: //oinky oinky oink //moo moo moo
####10.1.1.6 Split func Split(s, sep string) []string 功能:把s字符串按照sep分割,返回slice
示例代碼:
fmt.Printf("%q\n", strings.Split("a,b,c", ",")) fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a ")) fmt.Printf("%q\n", strings.Split(" xyz ", "")) fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins")) //運行結果: //["a" "b" "c"] //["" "man " "plan " "canal panama"] //[" " "x" "y" "z" " "] //[""]
####10.1.1.7 Trim func Trim(s string, cutset string) string 功能:在s字符串的頭部和尾部去除cutset指定的字符串 示例代碼:
fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! ")) //運行結果:["Achtung"]
####10.1.1.8 Fields func Fields(s string) []string 功能:去除s字符串的空格符,而且按照空格分割返回slice 示例代碼:
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz ")) //運行結果:Fields are: ["foo" "bar" "baz"]
###10.1.2 字符串轉換 字符串轉化的函數在strconv中,以下也只是列出一些經常使用的。
####10.1.2.1 Append Append 系列函數將整數等轉換爲字符串後,添加到現有的字節數組中。 示例代碼:
str := make([]byte, 0, 100) str = strconv.AppendInt(str, 4567, 10) //以10進制方式追加 str = strconv.AppendBool(str, false) str = strconv.AppendQuote(str, "abcdefg") str = strconv.AppendQuoteRune(str, '單')
fmt.Println(string(str)) //4567false"abcdefg"'單'
####10.1.2.2 Format Format 系列函數把其餘類型的轉換爲字符串。 示例代碼:
a := strconv.FormatBool(false) b := strconv.FormatInt(1234, 10) c := strconv.FormatUint(12345, 10) d := strconv.Itoa(1023)
fmt.Println(a, b, c, d) //false 1234 12345 1023 ####10.1.2.3 Parse Parse 系列函數把字符串轉換爲其餘類型。
示例代碼:
package main
import (
"fmt"
"strconv"
)
func checkError(e error) {
if e != nil {
fmt.Println(e)
}
}
func main() {
a, err := strconv.ParseBool("false")
checkError(err)
b, err := strconv.ParseFloat("123.23", 64)
checkError(err)
c, err := strconv.ParseInt("1234", 10, 64)
checkError(err)
d, err := strconv.ParseUint("12345", 10, 64)
checkError(err)
e, err := strconv.Atoi("1023")
checkError(err)
fmt.Println(a, b, c, d, e) //false 123.23 1234 12345 1023
}
複製代碼
##10.2 正則表達式 正則表達式是一種進行模式匹配和文本操縱的複雜而又強大的工具。雖然正則表達式比純粹的文本匹配效率低,可是它卻更靈活。按照它的語法規則,隨需構造出的匹配模式就可以從原始文本中篩選出幾乎任何你想要獲得的字符組合。
Go語言經過regexp標準包爲正則表達式提供了官方支持,若是你已經使用過其餘編程語言提供的正則相關功能,那麼你應該對Go語言版本的不會太陌生,可是它們之間也有一些小的差別,由於Go實現的是RE2標準,除了\C,詳細的語法描述參考:code.google.com/p/re2/wiki/…
其實字符串處理咱們可使用strings包來進行搜索(Contains、Index)、替換(Replace)和解析(Split、Join)等操做,可是這些都是簡單的字符串操做,他們的搜索都是大小寫敏感,並且固定的字符串,若是咱們須要匹配可變的那種就沒辦法實現了,固然若是strings包能解決你的問題,那麼就儘可能使用它來解決。由於他們足夠簡單、並且性能和可讀性都會比正則好。
示例代碼:
package main
import (
"fmt"
"regexp"
)
func main() {
context1 := "3.14 123123 .68 haha 1.0 abc 6.66 123."
//MustCompile解析並返回一個正則表達式。若是成功返回,該Regexp就可用於匹配文本。
//解析失敗時會產生panic
// \d 匹配數字[0-9],d+ 重複>=1次匹配d,越多越好(優先重複匹配d)
exp1 := regexp.MustCompile(`\d+\.\d+`)
//返回保管正則表達式全部不重疊的匹配結果的[]string切片。若是沒有匹配到,會返回nil。
//result1 := exp1.FindAllString(context1, -1) //[3.14 1.0 6.66]
result1 := exp1.FindAllStringSubmatch(context1, -1) //[[3.14] [1.0] [6.66]]
fmt.Printf("%v\n", result1)
fmt.Printf("\n------------------------------------\n\n")
context2 := `
<title>標題</title>
<div>你過來啊</div>
<div>hello mike</div>
<div>你大爺</div>
<body>呵呵</body>
`
//(.*?)被括起來的表達式做爲分組
//匹配<div>xxx</div>模式的全部子串
exp2 := regexp.MustCompile(`<div>(.*?)</div>`)
result2 := exp2.FindAllStringSubmatch(context2, -1)
//[[<div>你過來啊</div> 你過來啊] [<div>hello mike</div> hello mike] [<div>你大爺</div> 你大爺]]
fmt.Printf("%v\n", result2)
fmt.Printf("\n------------------------------------\n\n")
context3 := `
<title>標題</title>
<div>你過來啊</div>
<div>hello
mike
go</div>
<div>你大爺</div>
<body>呵呵</body>
`
exp3 := regexp.MustCompile(`<div>(.*?)</div>`)
result3 := exp3.FindAllStringSubmatch(context3, -1)
//[[<div>你過來啊</div> 你過來啊] [<div>你大爺</div> 你大爺]]
fmt.Printf("%v\n", result3)
fmt.Printf("\n------------------------------------\n\n")
context4 := `
<title>標題</title>
<div>你過來啊</div>
<div>hello
mike
go</div>
<div>你大爺</div>
<body>呵呵</body>
`
exp4 := regexp.MustCompile(`<div>(?s:(.*?))</div>`)
result4 := exp4.FindAllStringSubmatch(context4, -1)
/*
[[<div>你過來啊</div> 你過來啊] [<div>hello
mike
go</div> hello
mike
go] [<div>你大爺</div> 你大爺]]
*/
fmt.Printf("%v\n", result4)
fmt.Printf("\n------------------------------------\n\n")
for _, text := range result4 {
fmt.Println(text[0]) //帶有div
fmt.Println(text[1]) //不帶帶有div
fmt.Println("================\n")
}
}
複製代碼
##10.3 JSON處理 JSON (JavaScript Object Notation)是一種比XML更輕量級的數據交換格式,在易於人們閱讀和編寫的同時,也易於程序解析和生成。儘管JSON是JavaScript的一個子集,但JSON採用徹底獨立於編程語言的文本格式,且表現爲鍵/值對集合的文本描述形式(相似一些編程語言中的字典結構),這使它成爲較爲理想的、跨平臺、跨語言的數據交換語言。
開發者能夠用 JSON 傳輸簡單的字符串、數字、布爾值,也能夠傳輸一個數組,或者一個更復雜的複合結構。在 Web 開發領域中, JSON被普遍應用於 Web 服務端程序和客戶端之間的數據通訊。
Go語言內建對JSON的支持。使用Go語言內置的encoding/json 標準庫,開發者能夠輕鬆使用Go程序生成和解析JSON格式的數據。
JSON官方網站:www.json.org/ 在線格式化:www.json.cn/
###10.3.1 編碼JSON ####10.3.1.1 經過結構體生成JSON 使用json.Marshal()函數能夠對一組數據進行JSON格式的編碼。 json.Marshal()函數的聲明以下: func Marshal(v interface{}) ([]byte, error)
還有一個格式化輸出: // MarshalIndent 很像 Marshal,只是用縮進對輸出進行格式化 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
package main
import (
"encoding/json"
"fmt"
)
type IT struct {
Company string
Subjects []string
IsOk bool
Price float64
}
func main() {
t1 := IT{"itcast", []string{"Go", "C++", "Python", "Test"}, true, 666.666}
//生成一段JSON格式的文本
//若是編碼成功, err 將賦於零值 nil,變量b 將會是一個進行JSON格式化以後的[]byte類型
//b, err := json.Marshal(t1)
//輸出結果:{"Company":"itcast","Subjects":["Go","C++","Python","Test"],"IsOk":true,"Price":666.666}
b, err := json.MarshalIndent(t1, "", " ")
/*
輸出結果:
{
"Company": "itcast",
"Subjects": [
"Go",
"C++",
"Python",
"Test"
],
"IsOk": true,
"Price": 666.666
}
*/
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(string(b))
}
複製代碼
咱們看到上面的輸出字段名的首字母都是大寫的,若是你想用小寫的首字母怎麼辦呢?把結構體的字段名改爲首字母小寫的?JSON輸出的時候必須注意,只有導出的字段(首字母是大寫)纔會被輸出,若是修改字段名,那麼就會發現什麼都不會輸出,因此必須經過struct tag定義來實現。
針對JSON的輸出,咱們在定義struct tag的時候須要注意的幾點是: 字段的tag是"-",那麼這個字段不會輸出到JSON tag中帶有自定義名稱,那麼這個自定義名稱會出如今JSON的字段名中 tag中若是帶有"omitempty"選項,那麼若是該字段值爲空,就不會輸出到JSON串中 若是字段類型是bool, string, int, int64等,而tag中帶有",string"選項,那麼這個字段在輸出到JSON的時候會把該字段對應的值轉換成JSON字符串
示例代碼:
type IT struct {
//Company不會導出到JSON中
Company string `json:"-"`
// Subjects 的值會進行二次JSON編碼
Subjects []string `json:"subjects"`
//轉換爲字符串,再輸出
IsOk bool `json:",string"`
// 若是 Price 爲空,則不輸出到JSON串中
Price float64 `json:"price, omitempty"`
}
func main() {
t1 := IT{Company: "itcast", Subjects: []string{"Go", "C++", "Python", "Test"}, IsOk: true}
b, err := json.Marshal(t1)
//json.MarshalIndent(t1, "", " ")
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(string(b))
//輸出結果:{"subjects":["Go","C++","Python","Test"],"IsOk":"true","price":0}
}
複製代碼
####10.3.1.2 經過map生成JSON
// 建立一個保存鍵值對的映射
t1 := make(map[string]interface{})
t1["company"] = "itcast"
t1["subjects "] = []string{"Go", "C++", "Python", "Test"}
t1["isok"] = true
t1["price"] = 666.666
b, err := json.Marshal(t1)
//json.MarshalIndent(t1, "", " ")
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(string(b))
//輸出結果:{"company":"itcast","isok":true,"price":666.666,"subjects ":["Go","C++","Python","Test"]}
複製代碼
###10.3.2 解碼JSON 可使用json.Unmarshal()函數將JSON格式的文本解碼爲Go裏面預期的數據結構。
json.Unmarshal()函數的原型以下: func Unmarshal(data []byte, v interface{}) error
該函數的第一個參數是輸入,即JSON格式的文本(比特序列),第二個參數表示目標輸出容器,用於存放解碼後的值。
####10.3.2.1 解析到結構體
type IT struct {
Company string `json:"company"`
Subjects []string `json:"subjects"`
IsOk bool `json:"isok"`
Price float64 `json:"price"`
}
func main() {
b := []byte(`{
"company": "itcast",
"subjects": [
"Go",
"C++",
"Python",
"Test"
],
"isok": true,
"price": 666.666
}`)
var t IT
err := json.Unmarshal(b, &t)
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(t)
//運行結果:{itcast [Go C++ Python Test] true 666.666}
//只想要Subjects字段
type IT2 struct {
Subjects []string `json:"subjects"`
}
var t2 IT2
err = json.Unmarshal(b, &t2)
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(t2)
//運行結果:{[Go C++ Python Test]}
}
複製代碼
####10.3.2.2 解析到interface 示例代碼:
func main() {
b := []byte(`{
"company": "itcast",
"subjects": [
"Go",
"C++",
"Python",
"Test"
],
"isok": true,
"price": 666.666
}`)
var t interface{}
err := json.Unmarshal(b, &t)
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(t)
//使用斷言判斷類型
m := t.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case float64:
fmt.Println(k, "is float64", vv)
case bool:
fmt.Println(k, "is bool", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
}
複製代碼
運行結果:
##10.4 文件操做 ###10.4.1 相關api介紹 ####10.4.1.1 創建與打開文件 新建文件能夠經過以下兩個方法: func Create(name string) (file *File, err Error) 根據提供的文件名建立新的文件,返回一個文件對象,默認權限是0666的文件,返回的文件對象是可讀寫的。
func NewFile(fd uintptr, name string) *File 根據文件描述符建立相應的文件,返回一個文件對象
經過以下兩個方法來打開文件: func Open(name string) (file *File, err Error) 該方法打開一個名稱爲name的文件,可是是隻讀方式,內部實現其實調用了OpenFile。
func OpenFile(name string, flag int, perm uint32) (file *File, err Error) 打開名稱爲name的文件,flag是打開的方式,只讀、讀寫等,perm是權限
####10.4.1.2 寫文件 func (file *File) Write(b []byte) (n int, err Error) 寫入byte類型的信息到文件
func (file *File) WriteAt(b []byte, off int64) (n int, err Error) 在指定位置開始寫入byte類型的信息
func (file *File) WriteString(s string) (ret int, err Error) 寫入string信息到文件
####10.4.1.3 讀文件 func (file *File) Read(b []byte) (n int, err Error) 讀取數據到b中
func (file *File) ReadAt(b []byte, off int64) (n int, err Error) 從off開始讀取數據到b中
####10.4.1.4 刪除文件 func Remove(name string) Error 調用該函數就能夠刪除文件名爲name的文件
###10.4.2 示例代碼 ####10.4.2.1 寫文件
package main
import (
"fmt"
"os"
)
func main() {
fout, err := os.Create("./xxx.txt") //新建文件
//fout, err := os.OpenFile("./xxx.txt", os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer fout.Close() //main函數結束前, 關閉文件
for i := 0; i < 5; i++ {
outstr := fmt.Sprintf("%s:%d\n", "Hello go", i)
fout.WriteString(outstr) //寫入string信息到文件
fout.Write([]byte("abcd\n")) //寫入byte類型的信息到文件
}
}
複製代碼
xxx.txt內容以下:
####10.4.2.2 讀文件
func main() {
fin, err := os.Open("./xxx.txt") //打開文件
if err != nil {
fmt.Println(err)
}
defer fin.Close()
buf := make([]byte, 1024) //開闢1024個字節的slice做爲緩衝
for {
n, _ := fin.Read(buf) //讀文件
if n == 0 { //0表示已經到文件結束
break
}
fmt.Println(string(buf)) //輸出讀取的內容
}
}
複製代碼
###10.4.3 案例:拷貝文件 示例代碼:
package main
import (
"fmt"
"io"
"os"
)
func main() {
args := os.Args //獲取用戶輸入的全部參數
//若是用戶沒有輸入,或參數個數不夠,則調用該函數提示用戶
if args == nil || len(args) != 3 {
fmt.Println("useage : xxx srcFile dstFile")
return
}
srcPath := args[1] //獲取輸入的第一個參數
dstPath := args[2] //獲取輸入的第二個參數
fmt.Printf("srcPath = %s, dstPath = %s\n", srcPath, dstPath)
if srcPath == dstPath {
fmt.Println("源文件和目的文件名字不能相同")
return
}
srcFile, err1 := os.Open(srcPath) //打開源文件
if err1 != nil {
fmt.Println(err1)
return
}
dstFile, err2 := os.Create(dstPath) //建立目的文件
if err2 != nil {
fmt.Println(err2)
return
}
buf := make([]byte, 1024) //切片緩衝區
for {
//從源文件讀取內容,n爲讀取文件內容的長度
n, err := srcFile.Read(buf)
if err != nil && err != io.EOF {
fmt.Println(err)
break
}
if n == 0 {
fmt.Println("文件處理完畢")
break
}
//切片截取
tmp := buf[:n]
//把讀取的內容寫入到目的文件
dstFile.Write(tmp)
}
//關閉文件
srcFile.Close()
dstFile.Close()
}
複製代碼
運行結果: