本文目錄一覽:
- 1、如何將任意Golang接口轉換為字節數組
- 2、golang正則表達式 分組命名
- 3、golang怎麼返回結構體
- 4、GoLang中的切片擴容機制
- 5、golang變量(二)——map和slice詳解
- 6、如何理解Golang中的range語句
如何將任意Golang接口轉換為字節數組
golang語言本身就是c的工具集,開發c的程序用到的大部分結構體,內存管理,攜程等,golang基本都有,他只是在這個基礎上又加了一些概念這裡說一個很小的問題,就是字節數組轉string的問題,網上大部分都是這樣轉的(包括google上):string(p[:]),這個轉完了是有問題的,我們再來看一下string這個結構體:
struct String
{
byte* str;
intgo len;
};
這個結構體讓我想起了nginx的string,他是這樣定義的:
typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
golang裡邊 string的概念其實不是以前遇到\0結尾的概念了,他其實就是一塊連續的內存,首地址+長度,上面那樣賦值,如果p裡邊有\0,他不會做處理這個時候,如果再對這個string做其他處理就可能出問題了,比如strconv.Atoi轉成int就有錯誤,解決辦法就是需要自己寫一個正規的轉換函數:
func byteString(p []byte) string {
for i := 0; i len(p); i++ {
if p[i] == 0 {
return string(p[0:i])
}
}
return string(p)
}
這樣就不會出問題了
golang正則表達式 分組命名
正則中有分組這個功能,在golang中也可以使用命名分組。
一次匹配的情況
場景還原如下:
有一行文本,格式為:姓名 年齡 郵箱地址
請將其轉換為一個map
代碼實現如下:
str := `Alice 20 alice@gmail.com`
// 使用命名分組,顯得更清晰
re := regexp.MustCompile(`(?Pname[a-zA-Z]+)\s+(?Page\d+)\s+(?Pemail\w+@\w+(?:\.\w+)+)`)
match := re.FindStringSubmatch(str)
groupNames := re.SubexpNames()
fmt.Printf(“%v, %v, %d, %d\n”, match, groupNames, len(match), len(groupNames))
result := make(map[string]string)
// 轉換為map
for i, name := range groupNames {
if i != 0 name != “” { // 第一個分組為空(也就是整個匹配)
result[name] = match[i]
}
}
prettyResult, _ := json.MarshalIndent(result, “”, ” “)
fmt.Printf(“%s\n”, prettyResult)
輸出為:
[Alice 20 alice@gmail.com Alice 20 alice@gmail.com], [ name age email], 4, 4
{
“age”: “20”,
“email”: “alice@gmail.com”,
“name”: “Alice”
}
注意 [ name age email]有4個元素, 第一個為””。
多次匹配的情況
接上面的例子,實現一個更貼近現實的需求:
有一個文件, 內容大致如下:
Alice 20 alice@gmail.com
Bob 25 bob@outlook.com
gerrylon 26 gerrylon@github.com
…
更多內容
和上面一樣, 不過這次轉出來是一個slice of map, 也就是多個map。
代碼如下:
// 文件內容直接用字符串表示
usersStr := `
Alice 20 alice@gmail.com
Bob 25 bob@outlook.com
gerrylon 26 gerrylon@github.com
`
userRe := regexp.MustCompile(`(?Pname[a-zA-Z]+)\s+(?Page\d+)\s+(?Pemail\w+@\w+(?:\.\w+)+)`)
// 這裡要用FindAllStringSubmatch,找到所有的匹配
users := userRe.FindAllStringSubmatch(usersStr, -1)
groupNames := userRe.SubexpNames()
var result []map[string]string // slice of map
// 循環所有行
for _, user := range users {
m := make(map[string]string)
// 對每一行生成一個map
for j, name := range groupNames {
if j != 0 name != “” {
m[name] = strings.TrimSpace(user[j])
}
}
result = append(result, m)
}
prettyResult, _ := json.MarshalIndent(result, “”, ” “)
fmt.Println(string(prettyResult))
輸出為:
[
{
“age”: “20”,
“email”: “alice@gmail.com”,
“name”: “Alice”
},
{
“age”: “25”,
“email”: “bob@outlook.com”,
“name”: “Bob”
},
{
“age”: “26”,
“email”: “gerrylon@github.com”,
“name”: “gerrylon”
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
總結
使用命名分組可以使正則表示的意義更清晰。
轉換為map更加符合人類的閱讀習慣,不過比一般的根據索引取分組值麻煩一些。
————————————————
版權聲明:本文為CSDN博主「butterfly5211314」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:
golang怎麼返回結構體
用golang解析二進制協議時,其實沒必要管結構體的字段的對齊規則,何況語言規範也沒有規定如何對齊,也就是沒有規則。用encoding/binary.Read函數直接讀入struct里就行,struct就像c那樣寫
type Data struct {
Size, MsgType uint16
Sequence uint32
// …
}
golang編譯器加不加padding,Read都能正常工作,runtime知道Data的布局的,不像C直接做cast所以要知道怎樣對齊。
用unsafe.Alignof可以知道每個field的對齊長度,但沒必要用到。
package main
/*
#include stdint.h
#pragma pack(push, 1)
typedef struct {
uint16_t size;
uint16_t msgtype;
uint32_t sequnce;
uint8_t data1;
uint32_t data2;
uint16_t data3;
} mydata;
#pragma pack(pop)
mydata foo = {
1, 2, 3, 4, 5, 6,
};
int size() {
return sizeof(mydata);
}
*/
import “C”
import (
“bytes”
“encoding/binary”
“fmt”
“log”
“unsafe”
)
func main() {
bs := C.GoBytes(unsafe.Pointer(C.foo), C.size())
fmt.Printf(“len %d data %v\n”, len(bs), bs)
var data struct {
Size, Msytype uint16
Sequence uint32
Data1 uint8
Data2 uint32
Data3 uint16
}
err := binary.Read(bytes.NewReader(bs), binary.LittleEndian, data)
if err != nil {
log.Fatal(err)
}
fmt.Printf(“%v\n”, data) // {1 2 3 4 5 6}
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, data)
fmt.Printf(“%d %v\n”, buf.Len(), buf.Bytes()) // 15 [0 1 0 2 0 0 0 3 4 0 0 0 5 0 6]
}
GoLang中的切片擴容機制
[5]int 是數組,而 []int 是切片。二者看起來相似,實則是根本上不同的數據結構。
切片的數據結構中,包含一個指向數組的指針 array ,當前長度 len ,以及最大容量 cap 。在使用 make([]int, len) 創建切片時,實際上還有第三個可選參數 cap ,也即 make([]int, len, cap) 。在不聲明 cap 的情況下,默認 cap=len 。當切片長度沒有超過容量時,對切片新增數據,不會改變 array 指針的值。
當對切片進行 append 操作,導致長度超出容量時,就會創建新的數組,這會導致和原有切片的分離。在下例中
由於 a 的長度超出了容量,所以切片 a 指向了一個增長後的新數組,而 b 仍然指向原來的老數組。所以之後對 a 進行的操作,對 b 不會產生影響。
試比較
本例中, a 的容量為6,因此在 append 後並未超出容量,所以 array 指針沒有改變。因此,對 a 進行的操作,對 b 同樣產生了影響。
下面看看用 a := []int{} 這種方式來創建切片會是什麼情況。
可以看到,空切片的容量為0,但後面向切片中添加元素時,並不是每次切片的容量都發生了變化。這是因為,如果增大容量,也即需要創建新數組,這時還需要將原數組中的所有元素複製到新數組中,開銷很大,所以GoLang設計了一套擴容機制,以減少需要創建新數組的次數。但這導致無法很直接地判斷 append 時是否創建了新數組。
如果一次添加多個元素,容量又會怎樣變化呢?試比較下面兩個例子:
那麼,是不是說,當向一個空切片中插入 2n-1 個元素時,容量就會被設置為 2n 呢?我們來試試其他的數據類型。
可以看到,根據切片對應數據類型的不同,容量增長的方式也有很大的區別。相關的源碼包括: src/runtime/msize.go , src/runtime/mksizeclasses.go 等。
我們再看看切片初始非空的情形。
可以看到,與剛剛向空切片添加5個int的情況一致,向有3個int的切片中添加2個int,容量增長為6。
需要注意的是, append 對切片擴容時,如果容量超過了一定範圍,處理策略又會有所不同。可以看看下面這個例子。
具體為什麼會是這樣的變化過程,還需要從 源碼 中尋找答案。下面是 src/runtime/slice.go 中的 growslice 函數中的核心部分。
GoLang中的切片擴容機制,與切片的數據類型、原本切片的容量、所需要的容量都有關係,比較複雜。對於常見數據類型,在元素數量較少時,大致可以認為擴容是按照翻倍進行的。但具體情況需要具體分析。
golang變量(二)——map和slice詳解
衍生類型,interface{} , map, [] ,struct等
map類似於java的hashmap,python的dict,php的hash array。
常規的for循環,可以用for k,v :=range m {}. 但在下面清空有一個坑注意:
著名的map[string]*struct 副本問題
結果:
Go 中不存在引用傳遞,所有的參數傳遞都是值傳遞,而map是等同於指針類型的,所以在把map變量傳遞給函數時,函數對map的修改,也會實質改變map的值。
slice類似於其他語言的數組(list,array),slice初始化和map一樣,這裡不在重複
除了Pointer數組外,len表示使用長度,cap是總容量,make([]int, len, cap)可以預申請 比較大的容量,這樣可以減少容量拓展的消耗,前提是要用到。
cap是計算切片容量,len是計算變量長度的,兩者不一樣。具體例子如下:
結果:
分析:cap是計算當前slice已分配的容量大小,採用的是預分配的夥伴算法(當容量滿時,拓展分配一倍的容量)。
append是slice非常常用的函數,用於添加數據到slice中,但如果使用不好,會有下面的問題:
預期是[1 2 3 4 5 6 7 8 9 10], [1 2 3 4 5 6 7 8 9 10 11 12],但實際結果是:
注意slice是值傳遞,修改一下:
輸出如下:
== 只能用於判斷常規數據類型,無法使用用於slice和map判斷,用於判斷map和slice可以使用reflect.DeepEqual,這個函數用了遞歸來判斷每層的k,v是否一致。
當然還有其他方式,比如轉換成json,但小心有一些異常的bug,比如html編碼,具體這個json問題,待後面在分析。
如何理解Golang中的range語句
你把它理解為遍歷么,結合for循環。
假設有一個初始化好的數組(table)或者切片(slice)的table,且table長度為10:
for i, value := range table {
fmt.Printf(“i=%v, value=%v\n”, i, value)
}
則會執行fmt.Printf10次,且這10次的【i】的值分別是從0~9,也就相當於
for i := 0; i len(table); i++ {
fmt.Printf(“i=%v, value=%v\n”, i, table[i])
}
如果把上邊的數組或者切片換成map
for key, value := range table {
fmt.Pritnf(“key=%v, value=%v\n”, key, value)
}
則類似上邊的過程把map裡邊的key-value鍵值對一 一遍歷
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/227777.html