{"id":13756319,"url":"https://github.com/jackhu1990/golangman","last_synced_at":"2026-02-23T14:42:13.938Z","repository":{"id":44349151,"uuid":"52487804","full_name":"jackhu1990/golangman","owner":"jackhu1990","description":"golangman是一个帮助具有其他语言开发经验的人快速上手golang的开源项目.","archived":false,"fork":false,"pushed_at":"2020-02-15T07:19:55.000Z","size":829,"stargazers_count":249,"open_issues_count":0,"forks_count":55,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-05-10T03:31:50.392Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jackhu1990.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-25T01:37:50.000Z","updated_at":"2025-03-27T02:17:56.000Z","dependencies_parsed_at":"2022-09-13T07:52:01.990Z","dependency_job_id":null,"html_url":"https://github.com/jackhu1990/golangman","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jackhu1990/golangman","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackhu1990%2Fgolangman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackhu1990%2Fgolangman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackhu1990%2Fgolangman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackhu1990%2Fgolangman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jackhu1990","download_url":"https://codeload.github.com/jackhu1990/golangman/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackhu1990%2Fgolangman/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29745991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-23T07:44:07.782Z","status":"ssl_error","status_checked_at":"2026-02-23T07:44:07.432Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T11:00:41.535Z","updated_at":"2026-02-23T14:42:13.916Z","avatar_url":"https://github.com/jackhu1990.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# 第零章 说明\n- golangman中golang表示语言,man表示linux man命令.\n- 本项目针对具有一定开发经验的开发者,不面向初学者.\n- 如同man命令一样,本项目不能代替官方完整文档,更多的起到了解、帮助与速查的功能.\n- 本项目可任意转载,但必须指明出处且不得删除本说明.\n- 如果本项目对你有帮助,请分享给更多的人.https://github.com/jackhu1990/golangman.\n\n# 第一章 快速开始\n\n## 下载golang安装包\n- [google官方地址](https://golang.org/dl/)\n- [墙内地址](http://golangtc.com/download)\n- 安装后目录结构\n![](img/安装后目录.png)\n![](img/安装后目录bin.png)\n\n## 配置环境变量\n- GOROOT\n目的是告诉一些其他程序(如IDE)golang安装包安装在哪里\n![](img/GOROOT.png)\n- PATH\n目的是可以系统中直接运行go.exe,而无需进入到安装目录\n![](img/path.png)\n- GOPATH\n目的是指定go包的安装目录.（注意：在学习第四章之前，这样理解是没问题的。）\ngolang的包类似java的jar、c++的lib.golang的包管理类似nodejs的npm、java的maven、c#的nuget.\n除了系统自带的一些包,其他包均需要下载.\n![](img/gopath.png)\n![](img/gopath目录.png)\n\n## 安装IDE\n个人喜好,因为我很喜欢WebStorm开发js,所以安装了WebStorm的go插件,个人可自行改变,但根据我试用多个IDE或代码编辑器的情况,强烈推荐WebStorm,其他ide光是调试功能就能把人玩死.\n- WebStorm 11以上版本\n- 安装 WebStorm使用的go插件\n    ![](img/go插件.png)\n\n## Hello GOLang\n- 新建一个hellogolang.go文件\n```go\n     package main\n     import \"fmt\"\n\n     func main() {\n     \tfmt.Println(\"Hello Golang\")\n     }\n```\n- WebStorm 11 会要求配置下go目录,按照步骤设置.ps:这好像一个bug.\n![](img/设置SDK.png)\n\n- 鼠标右键,RUN.\n![](img/hellogolang.png)\n\n# 第二章 语言只是语言\n\n## 基础数据类型\n```go\n\nint8 int16 int32 int64\nuint8 uint16\nfloat32 float64\nstring\nbool\n\n```\n\n## 结构体\n```go\n\ntype PersonBase struct{\n\tName string\n\tAge int\n}\ntype Boy struct{\n\tPerson PersonBase\n\tSex string\n}\n\n\nvar person PersonBase\n\n```\ntype 类似c/c++中 typedef\n\n## 变量定义\n- var\n```go\nvar inputCount  uint32\nvar(\n    inputCount  uint32\n    outputCount uint32\n    errorCount  uint32\n)\ninputCount = 1024\n```\n- var简写\n```go\n\ninputCount := 1024\n\n```\n\n## 这里应该有指针\n```go\n\n    var inputCountP *int\n    var person PersonBase\n    var personP *PersonBase\n    person.Name = \"jackhu\"\n    person.Age = 27\n    personP = new(PersonBase)\n    personP.Name = \"gaofei\"\n    personP.Age = 27\n\n```\n\n## 常用复合数据类型\n```go\n\na := [...]string   {\"one\",\"two\"} //array\ns := []string      {\"one\",\"two\"} //slice\nm := map[int]string{1:\"one\", 2:\"two\"} //map\n\n```\n- array和slice\n\n    和c有区别,在Go中，数组是值。\n    将一个数组赋予另一个数组会复制其所有元素。so,若将某个数组传入某个函数，它将接收到该数组的一份副本而非指针。\n    数组的大小是其类型的一部分。类型 [10]int 和 [20]int 是不同的。\n    数组是固定分配,不可更改.在详细规划内存布局时，数组是非常有用的，有时还能避免过多的内存分配， 但它们主要用作切片的构件。\n    切片保存了对底层数组的引用，若你将某个切片赋予另一个切片，它们会引用同一个数组。 若某个函数将一个切片作为参数传入，则它对该切片元素的修改对调用者而言同样可见， 这可以理解为传递了底层数组的指针。\n    简单来说,数组是值,切片类似指针,指向数组.其他遵循值拷贝.\n\n\n```go\n\nfunc test(slice []string)(){\n\tslice[2]=\"y\"\n\treturn\n}\nfunc main() {\n\ts := []string{\"1\",\"2\",\"3\"}\n\ts1:= s;\n\tfor i,v:=range s {\n\t\tfmt.Println(i, v)\n\t}\n\ts1[2]=\"w\";\n\tfor i,v:=range s {\n\t\tfmt.Println(i, v)\n\t}\n\ttest(s1)\n\tfor i,v:=range s {\n\t\tfmt.Println(i, v)\n\t}\n}\n    0 1\n    1 2\n    2 3\n    0 1\n    1 2\n    2 w\n    0 1\n    1 2\n    2 y\n\n\n```\n\n- chan\n\n管道符号\u003c-\n简单理解:通道的目的就是为了go程序线程同步用的,类似管道pipe.\n一边流入,一边流出.流出检测时如果没有数据,则一直阻塞住.\n某种程度上可以把通道当作一种锁来用().当然golang自身有mutex的包.\n\n[详细信息](http://www.imooc.com/code/7555)\n\n- make\n内建函数 make(T, args) 的目的不同于 new(T)。它只用于创建切片（slice）、映射(map)和(chan)，并返回类型为 T（而非 *T）的一个已初始化 （而非置零）的值。 出现这种用差异的原因在于，这三种类型本质上为引用数据类型，它们在使用前必须初始化。 例如，切片是一个具有三项内容的描述符，包含一个指向（数组内部）数据的指针、长度以及容量， 在这三项被初始化之前，该切片为 nil。对于切片、映射和信道，make 用于初始化其内部的数据结构并准备好将要使用的值。\n[详细信息](http://docscn.studygolang.com/doc/effective_go.html#make分配)\n\n## 条件控制语句\n- if\n```go\n    if i := 10; i \u003c 5 {\n        fmt.Print(\"***********\")\n    }\n```\n- switch\n\n和c/c++不同,go中switch更加现代化,支持string之类的类型.同时需要注意,go中的switch不需要主动写break.\n```go\n\nvar name string\nswitch name {\ncase \"Golang\":\n    fmt.Println(\"A programming language from Google.\")\ncase \"Rust\":\n    fmt.Println(\"A programming language from Mozilla.\")\ndefault:\n    fmt.Println(\"Unknown!\")\n}\n\n```\n[更多switch用法](http://docscn.studygolang.com/doc/effective_go.html#switch)\n\n- select\n\nsocket编程中的select函数在概念上类似,更加简化.不懂select的先学习socket编程中的select,可以很好的理解.\n```go\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch4 := make(chan int, 1)\n\tch4 \u003c- 1\n\tfor i := 0; i \u003c 4; i++ {\n\t\tselect {\n\t\tcase e, ok := \u003c-ch4:\n\t\t\tif !ok {\n\t\t\t\tfmt.Println(\"End.\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfmt.Println(e)\n\n\t\tdefault:\n\t\t\tfmt.Println(\"No Data!\")\n\t\t\tclose(ch4)\n\t\t}\n\t}\n}\n    1\n    No Data!\n    End.\n\n```\n\n- for\n```go\n\n    sum := 0\n    for i := 0; i \u003c 10; i++ {\n        sum += i\n    }\n\tarr :=[...] int {1,2,3,4,5}\n\tfor i,v:= range arr{\n\t\tfmt.Println(\"index:\", i, \"value:\", v)\n\t}\n\tm := map[int]string{1:\"one\", 2:\"two\"}\n\tfor i,v:= range m{\n\t\tfmt.Println(\"index:\", i, \"value:\", v)\n\t}\n    index: 0 value: 1\n    index: 1 value: 2\n    index: 2 value: 3\n    index: 3 value: 4\n    index: 4 value: 5\n    index: 1 value: one\n    index: 2 value: two\n\n```\n\n##interface{}类型\ninterface{}是一个非常特殊，非常强大的通用类型。任何类型都可以赋值给interface{}类型。interface{}当然也可以在转换成其他的类型。\n以我的经验来看，interface{}被我使用最多是用来处理json的解析工作和容错处理。在传统的语言中，我们必须事先明确的知道json中value的值的类型，\n否则，我们就必须在解析的时候事先判断数据类型。总之，虽然可以处理，但也非常麻烦。\n- 使用interface{}接收json\n```\n//这样再也不必要纠结value的具体类型，直接使用interface{}接受即可。\ntype Point struct {\n\tM string  `json:\"measure\"`\n\tV interface{} `json:\"vavlue\"`\n\tP int\t`json:\"type\"`\n}\n\n type PointsRT struct {\n \tTopic string \t`json:\"topic\"`\n \tSt   int64\t`json:\"st\"`\n \tData []Point\t`json:\"data\"`\n \tVer  float64\t`json:\"ver\"`\n }\n\nvar pointsRT PointsRT\n\t//@todo json error\n\tjson.Unmarshal(message.Payload(), \u0026pointsRT)\n\n```\n- 使用interface{}转化json\n```\n\t\tvar pointsRT PointsRT; //构造发送结构\n\t\tpointsRT.Topic = wsTopic;\n\t\tpointsRT.St = time.Now().Unix()\n\t\tpointsRT.Data = make([]Point, 0)\n\t\tpointsRT.Data = append(pointsRT.Data, point)\n\t\tpointsRT.Ver = 1.0\n\t\tjson, err := json.Marshal(pointsRT)\n\t\tif err != nil {\n\t\t\tlog.Println(\"json err:\", err)\n\t\t\tcontinue\n\t\t}\n\n```\n\n判断interface{}实际存储的数据结构，并使用其中的数据\n```go\n//类型选择是类型转换的一种形式：它接受一个接口，在选择 （switch）中根据其判断选择对应的情况（case）， 并在某种意义上将其转换为该种类型。\n// 若它已经为字符串，我们需要该接口中实际的字符串值； \n\nvar jsonValue interface{} // 调用者提供的其他类型值,需要转换成接口类型。\nswitch value := jsonValue.(type) {\n case int:\n      fmt.Printf(\"value is %d\\n\", value)\n case string:\n      fmt.Printf(\"value is %s\\n\", value)\n}\n\n```\nComma-ok断言\nGo语言里面有一个语法，可以直接判断是否是该类型的变量： value, ok = element.(T)，这里value就是变量的值，ok是一个bool类型，element是interface变量，T是断言的类型。\n如果element里面确实存储了T类型的数值，那么ok返回true，否则返回false。\n```\nvar jsonValue interface{} // 调用者提供的其他类型值,需要转换成接口类型。\n if value, ok := jsonValue.(int); ok {\n            fmt.Printf(\"value is %d\\n\", value)\n        } \n```\n\n\n## 函数\n- 定义\n```go\n\nfunc Add(addA int, addB int) (int){\n    result := addA + addB\n    return result\n}\n\n```\n- 两个返回值\n```go\n\nfunc Add(addA int, addB int) (int, error){\n    if addA==0 {\n        return 0,errors.New(\"第一个参数不能为0\")\n    }\n    result := addA + addB\n    return result,nil\n}\nfunc main() {\n        fmt.Println(\"Hello Golang\")\n        result,err := Add(0,10)\n        if err != nil {\n            fmt.Println(err)\n            return\n        }\n        fmt.Println(result)\n}\n    Hello Golang\n    第一个参数不能为0\n\n```\n- 对返回值进行预赋值\ngo中可以使用命名返回值，可以直接在任意的地方给返回值赋值，在最终需要返回的地方直接return，返回值会自动加上\n```\n\nfunc Add(addA int, addB int) (result int, err error){\n    if addA==0 {\n        result = 0\n\terr = errors.New(\"第一个参数不能为0\")\n    }\n    result := addA + addB\n    return\n}\n```\n\n## 这里没有类吗\n没有类,但是有结构体,有结构体函数（我喜欢这么叫）.结构体函数可以用来模拟类的功能.\ngoogle官方编程规范规定,包和结构体中变量名大写表示对外暴露,小写表示不对外暴露.相当于其他语言共有私有标识.此规则很多第三方接口遵守,请也遵守.\n- 结构体函数\n```go\n\npackage main\n\nimport \"fmt\"\n\ntype Cat struct {\n\tName    string\n\tAge     int32\n\tAddress string\n}\n\nfunc (cat *Cat) Grow() {\n\tcat.Age++\n}\nfunc main() {\n\tmyCat := Cat{\"Little C\", 2, \"In the house\"}\n\tmyCat.Grow()\n\tfmt.Printf(\"%v\", myCat)\n}\n\n{Little C 3 In the house}\n\n```\n\n严格意义上来说,go没有属于结构体的函数.go函数有个特性,可以把函数归于任何类型(或任何类型的指针),相当于把函数的归属权付给了某人,某人可以直接调用函数.\n如上面的例子中,函数把归属权给了结构体指针,结构体就可以调用这个函数,同时在函数内部使用这个结构体(结构体的名字当作this指针用).\n另外,如果函数的归属的是结构体(而不是指针),那么结构体照样能用,但是结构体函数内修改将不能影响外部(传递的都是副本,但指针的副本解引用会是指向源对象)\n请仔细理解下面的例子,至关重要,请手敲一遍理解.\n\n```go\n\npackage main\n\nimport \"fmt\"\n\ntype Handle int64\nfunc (h Handle) Show1(i int64) int64{\n    h = 1\n    return i + int64(h)\n}\nfunc (h *Handle) Show2(i int64) int64{\n    *h = 2\n    return i + int64(*h)\n}\n\nfunc main() {\n    var hand Handle = 0\n    fmt.Println(hand.Show1(100))\n    fmt.Println(hand)\n\n    fmt.Println(hand.Show2(100))\n    fmt.Println(hand)\n}\n\n101\n0\n102\n2\n\n```\n\n\n\n\n- interface\n请注意interface和interface{}是两个东西\ninterface是一组method的组合，我们通过interface来定义对象的一组行为。\ngo interface效果和java的interface,c++的抽象类相同。\ngo中接口的继承不用显示声明.go中规定,接口不用显示声明,结构体函数如果实现接口的函数,就相当于类继承了这个接口,请结合代码理解.\n```go\n\npackage main\n\nimport \"fmt\"\n\ntype Animal interface {\n\tGrow()\n\tMove(string) string\n}\ntype Cat struct{\n\tName string\n\tAge int32\n\tAddress string\n}\nfunc (cat *Cat) Grow(){\n\tcat.Age++\n}\nfunc (cat *Cat)Move(newAdddress string)(oldAddress string){\n\toldAddress = cat.Address\n\tcat.Address = newAdddress\n\treturn\n}\n\nfunc main() {\n\tmyCat := Cat{\"Little C\", 2, \"In the house\"}\n\tanimal, ok := interface{}(\u0026myCat).(Animal)\n\tanimal.Grow()\n\tfmt.Printf(\"%v, %v\\n\", ok, animal)\n}\ntrue, \u0026{Little C 3 In the house}\n\n```\n- 接口的类型转换高级用法\ninterface{}可以转换成任何类型，自然也包括含有interce的类型（）\n关于\"接口的类型转换\"这个知识点请仔细理解.\n\n```go\n//类型选择是类型转换的一种形式：它接受一个接口，在选择 （switch）中根据其判断选择对应的情况（case）， 并在某种意义上将其转换为该种类型。\n// 若它已经为字符串，我们需要该接口中实际的字符串值； 若它有 String 方法，我们则需要调用该方法所得的结果。\ntype MyStringer interface{\n\tString() string\n}\nvar value interface{} // 调用者提供的其他类型值,需要转换成接口类型。\nswitch str := value.(type) {\ncase string:\n\treturn str\ncase MyStringer:\n\treturn str.String()\n}\n\n```\n\n\u003e按照约定，只包含一个方法的接口应当以该方法的名称加上-er后缀或类似的修饰来构造一个施动着名词，如 Reader、Writer、 Formatter、CloseNotifier 等。\n\n## defer\n就是javascript中的promise模式,angularjs中的$q服务.这里比他们都要简单.\ndefer的语句保证在函数return之后执行.\n非常优秀的设计,简化了异常情况下资源释放的问题.比如文件打开,打开后就defer close.这样即使有异常,文件也会被关闭.\n```go\n\nfile,err:=os.Open(fullPath)\nif err!=nil{\n    return nil,err\n}\ndefer file.Close()\n```\n\n## go程\n\ngo程相当轻量级线程,其实是croutine的go实现.相当golang语言本身集成了线程库的一些功能.同时,go程是golang高并发的核心.\n\ngo程 chan make slice 综合小例子\n```go\n\npackage main\n\nimport \"fmt\"\n\nfunc sum(a []int, c chan int) {\n\tsum := 0\n\tfor _, v := range a {\n\t\tsum += v\n\t}\n\tc \u003c- sum //将和送入c\n}\n\nfunc main() {\n\ta := []int{1, 2, 3, 4, 5, 6}\n\n\tc := make(chan int)\n\n\tgo sum(a[len(a)/2:], c)\n\tgo sum(a[:len(a)/2], c)\n\n\tx, y := \u003c-c, \u003c-c //从c中获取\n\n\tfmt.Println(x,\"+\", y,\"=\", x+y)\n}\n\n\n```\n谈到线程就避免不了锁的概念,golang中有mutex,适当的使用chan也是一种不错的选择,用法和其他语言所得用法一样,这里就不举例了.\n\n## 恐慌panic与恢复recover\n类似c++,java中的异常.panic就是抛出异常,recover就是捕获异常.\n```go\n\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc innerFunc() {\n\tdefer func(){ //尝试移动本段代码查看效果\n\t\tif p:=recover(); p!=nil{\n\t\t\tfmt.Printf(\"Fatal error: %s\\n\",p)\n\t\t}\n\t}()\n\tfmt.Println(\"Enter innerFunc\")\n\tpanic(errors.New(\"Occur a panic!\"))\n\tfmt.Println(\"Quit innerFunc\")\n}\n\nfunc outerFunc() {\n\n\tfmt.Println(\"Enter outerFunc\")\n\tinnerFunc()\n\tfmt.Println(\"Quit outerFunc\")\n}\n\nfunc main() {\n\tfmt.Println(\"Enter main\")\n\touterFunc()\n\tfmt.Println(\"Quit main\")\n}\n\n    Enter main\n    Enter outerFunc\n    Enter innerFunc\n    Fatal error: Occur a panic!\n    Quit outerFunc\n    Quit main\n\n\n```\n\n## 说下包\n\n包可以自定义,可以从网上下载.\n从网上下载github上的[websocket包](github.com/gorilla/websocket),在命令行上敲入\n```\ngo get github.com/gorilla/websocket\n```\n\n## 第三方go包\n网上很多的go包都是运用cgo的技术生成的，比如常用gozmq包，了解下里面的原理，对于怎么在windows、linux上跨平台使用包很有帮助，不然这里面的坑就会把你玩死。\ncgo简单来说就是一种go调用c的技术，原理就是先写一层wrapper，再在里面调用c库，最后封装成go包。[详情](http://googollee.blog.163.com/blog/static/1159411201031812128593/)\n这里面需要注意，go包本身是跨平台的，但是里面实际调用的c库却是不跨平台的（windows是windows的库，linux是linux的库，如果涉及到动态链接库，windows上需要在生成的go程序同级配套dll，linux需要配套so）\ngithub上的很多go包，在linux上安装可能很简单，比如[zmq4]（https://github.com/pebbe/zmq4）\n按照提示先下载libsodium源码，configure、make、make install ；再下载zmq源码configure、make、make install然后go get github.com/pebbe/zmq4,这样包就安装成功了。\n而在windows上，因为使用的cgo，要求在windows上安装一个gcc的编译环境，这里安装的mingw64，然后下载windos上的库，再把.h .lib 文件分别拷贝到mingw64安装目录的include，lib目录，还要把lib名字改成和.h名字对应的名字（zmq.h 对应zmq.lib），然后包才有可能安装成功。\n最后运行时，需要把dll或者so文件拷贝到go程序目录里。\n\n这里总结，纯go包跨平台，cgo不算跨平台。\n![windows](img/mingw.png)\n![linux](img/linuxlib.png)\n\n\n## 更多知识点\n\n- 接口类型断言 .() \n    x.(y) 断言x中的条目实现了y\n\n## [FAQ](http://docscn.studygolang.com/doc/faq#Google使用Go)\n\n## [中文站文档资料站](http://docscn.studygolang.com/doc/)\n\n\n# 第三章 从写些小工具开始\n\n- 字符串操作\n```go\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tname := \"harry potty\"\n\tarr := strings.Split(name, \" \")\n\tfmt.Println(arr)\n\tarr2 := strings.Join(arr, \"#\")\n\tfmt.Println(arr2)\n\tstr := strings.Replace(name, \"harry\", \"\\\\t\", -1)\n\tfmt.Println(str)\n}\n\n[harry potty]\nharry#potty\n\\t potty\n\n\n```\n- json操作\n\n```go\n\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\ntype Person struct {\n\tName string\n\tAge  int\n\tsecret string\n}\ntype Girl struct {\n\tPerson Person\n\tSexy    bool\n}\n\nfunc main() {\n\tdefer func() {\n\t\tfmt.Println(\"程序结束了\")\n\t}()\n\tperson := Person{\"胡彦春\", 18, \"秘密是小写的,不会出现在json中,很有意思的特性\"}\n\tgilr := Girl{person, true}\n\tb, err := json.Marshal(gilr)\n\tif err != nil {\n\t\tfmt.Print(err)\n\t}\n\tfmt.Print(string(b))\n\n\n}\n\n{\"Person\":{\"Name\":\"胡彦春\",\"Age\":18},\"Sexy\":true}程序结束了\n\n```\n\n这里面加上一个json嵌套json字符串（json里面的字段类型是字符串，字符串内容是json），便于理解golang Json包的强大\n![json混合json](img/json混合json.png)\n\n\n- httpServer\n```go\n\npackage main\n\nimport (\n\"net/http\"\n)\n\nfunc SayHello(w http.ResponseWriter, req *http.Request) {\nw.Write([]byte(\"Hello\"))\n}\n\nfunc main() {\nhttp.HandleFunc(\"/hello\", SayHello)\nhttp.ListenAndServe(\":8001\", nil)\n\n}\n\n访问 http://localhost:8001/hello 返回Helo\n\n```\n\n- http Get Post操作\n```go\n\npackage main\n\nimport (\n\t\"net/http\"\n\t\"io/ioutil\"\n\t\"fmt\"\n)\n\nfunc httpGet() {\n\tresp, err := http.Get(\"http://localhost:8001/hello\")\n\tif err != nil {\n\t\t// handle error\n\t}\n\n\tdefer resp.Body.Close()\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\t// handle error\n\t}\n\n\tfmt.Println(string(body))\n}\n\n```\n[POST操作等更多信息](http://www.01happy.com/golang-http-client-get-and-post/)\n\n\n- 文件操作\n\n基本上和c操作文件没有大区别,概念都一样\n\n```go\n\npackage main\n\nimport (\n    \"bufio\"\n    \"bytes\"\n    \"fmt\"\n    \"io\"\n    \"os\"\n    \"path/filepath\"\n)\n\nfunc read(r io.Reader) ([]byte, error) {\n    br := bufio.NewReader(r)\n    var buf bytes.Buffer\n    for {\n        ba, isPrefix, err := br.ReadLine()\n        if err != nil {\n            if err == io.EOF {\n                break\n            }\n        }\n        buf.Write(ba)\n        if !isPrefix {\n            buf.WriteByte('\\n')\n        }\n    }\n    return buf.Bytes(), nil\n}\nfunc readFile(filename string) ([]byte, error) {\n    parentPath, err := os.Getwd()\n    if err != nil {\n        return nil, err\n    }\n    fullPath := filepath.Join(parentPath, filename)\n    file, err := os.Open(fullPath)\n    if err != nil {\n        return nil, err\n    }\n    defer file.Close()\n    return read(file)\n}\nfunc main() {\n    fileName := \"example/FileTest.go\"\n    data, err := readFile(fileName)\n    if err != nil {\n        fmt.Printf(\"erris%v\", err)\n    }\n    fmt.Printf(\"Thecontentof'%s':\\n%s\\n\", fileName, data)\n}\n\n\n```\n\n- redis\n\n\n```go\n\npackage main\n\nimport (\n    \"github.com/garyburd/redigo/redis\"\n    \"fmt\"\n    \"time\"\n)\n\nfunc main() {\n    conn , err := redis.DialTimeout(\"tcp\", \"127.0.0.1:6379\", 0, 1*time.Second, 1*time.Second)\n    if err != nil {\n        panic(err)\n    }\n    defer conn.Close()\n    size ,err:= conn.Do(\"DBSIZE\")\n    fmt.Printf(\"size is %d \\n\",size)\n\n    _,err = conn.Do(\"SET\",\"user:user0\",123)\n    _,err = conn.Do(\"SET\",\"user:user1\",456)\n    _,err = conn.Do(\"APPEND\",\"user:user0\",87)\n\n    user0,err := redis.Int(conn.Do(\"GET\",\"user:user0\"))\n    user1,err := redis.Int(conn.Do(\"GET\",\"user:user1\"))\n\n    fmt.Printf(\"user0 is %d , user1 is %d \\n\",user0,user1)\n}\n\n\n```\n\n[更多例子](https://github.com/garyburd/redigo/tree/master/redis)\n\n- websocket\n```go\n\npackage main\n\nimport (\n    \"flag\"\n    \"html/template\"\n    \"log\"\n    \"net/http\"\n\n    \"github.com/gorilla/websocket\"\n)\n\nvar addr = flag.String(\"addr\", \"localhost:8080\", \"http service address\")\n\nvar upgrader = websocket.Upgrader{} // use default options\n\nfunc echo(w http.ResponseWriter, r *http.Request) {\n    c, err := upgrader.Upgrade(w, r, nil)\n    if err != nil {\n        log.Print(\"upgrade:\", err)\n        return\n    }\n    defer c.Close()\n    for {\n        mt, message, err := c.ReadMessage()\n        if err != nil {\n            log.Println(\"read:\", err)\n            break\n        }\n        log.Printf(\"recv: %s\", message)\n        err = c.WriteMessage(mt, message)\n        if err != nil {\n            log.Println(\"write:\", err)\n            break\n        }\n    }\n}\n\nfunc home(w http.ResponseWriter, r *http.Request) {\n\thomeTemplate.Execute(w, \"ws://\"+r.Host+\"/echo\")\n}\n\nfunc main() {\n    flag.Parse()\n    log.SetFlags(0)\n    http.HandleFunc(\"/echo\", echo)\n    http.HandleFunc(\"/\", home)\n    log.Fatal(http.ListenAndServe(*addr, nil))\n}\n\nvar homeTemplate = template.Must(template.New(\"\").Parse(`\n\u003c!DOCTYPE html\u003e\n    \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cscript\u003e\n    window.addEventListener(\"load\", function(evt) {\n        var output = document.getElementById(\"output\");\n        var input = document.getElementById(\"input\");\n        var ws;\n        var print = function(message) {\n            var d = document.createElement(\"div\");\n            d.innerHTML = message;\n            output.appendChild(d);\n        };\n        document.getElementById(\"open\").onclick = function(evt) {\n            if (ws) {\n                return false;\n            }\n            ws = new WebSocket(\"{{.}}\");\n            ws.onopen = function(evt) {\n                print(\"OPEN\");\n            }\n            ws.onclose = function(evt) {\n                print(\"CLOSE\");\n                ws = null;\n            }\n            ws.onmessage = function(evt) {\n                print(\"RESPONSE: \" + evt.data);\n            }\n            ws.onerror = function(evt) {\n                print(\"ERROR: \" + evt.data);\n            }\n            return false;\n        };\n        document.getElementById(\"send\").onclick = function(evt) {\n            if (!ws) {\n                return false;\n            }\n            print(\"SEND: \" + input.value);\n            ws.send(input.value);\n            return false;\n        };\n        document.getElementById(\"close\").onclick = function(evt) {\n            if (!ws) {\n                return false;\n            }\n            ws.close();\n            return false;\n        };\n    });\n    \u003c/script\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n    \u003ctable\u003e\n        \u003ctr\u003e\n            \u003ctd valign=\"top\" width=\"50%\"\u003e\n                \u003cp\u003eClick \"Open\" to create a connection to the server,\n                \"Send\" to send a message to the server and \"Close\" to close the connection.\n                You can change the message and send multiple times.\n                \u003cp\u003e\n            \u003cform\u003e\n                \u003cbutton id=\"open\"\u003eOpen\u003c/button\u003e\n                \u003cbutton id=\"close\"\u003eClose\u003c/button\u003e\n                \u003cp\u003e\u003cinput id=\"input\" type=\"text\" value=\"Hello world!\"\u003e\n                \u003cbutton id=\"send\"\u003eSend\u003c/button\u003e\n            \u003c/form\u003e\n            \u003c/td\u003e\n            \u003ctd valign=\"top\" width=\"50%\"\u003e\n                \u003cdiv id=\"output\"\u003e\u003c/div\u003e\n            \u003c/td\u003e\n        \u003c/tr\u003e\n        \u003c/table\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n`))\n\n```\n[更多websocket例子](https://github.com/gorilla/websocket/tree/master/examples)\n\n- options\n比自带的flag更强大的命令行工具,帮助服务器程序自动生成命令行\n\n```go\n\npackage main\n\nimport (\n    \"github.com/voxelbrain/goptions\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    options := struct {\n        Servers  []string      `goptions:\"-s, --server, obligatory, description='Servers to connect to'\"`\n        Password string        `goptions:\"-p, --password, description='Don\\\\'t prompt for password'\"`\n        Timeout  time.Duration `goptions:\"-t, --timeout, description='Connection timeout in seconds'\"`\n        Help     goptions.Help `goptions:\"-h, --help, description='Show this help'\"`\n\n        goptions.Verbs\n        Execute struct {\n            Command string   `goptions:\"--command, mutexgroup='input', description='Command to exectute', obligatory\"`\n            Script  *os.File `goptions:\"--script, mutexgroup='input', description='Script to exectute', rdonly\"`\n        } `goptions:\"execute\"`\n        Delete struct {\n            Path  string `goptions:\"-n, --name, obligatory, description='Name of the entity to be deleted'\"`\n            Force bool   `goptions:\"-f, --force, description='Force removal'\"`\n        } `goptions:\"delete\"`\n    }{ // Default values goes here\n        Timeout: 10 * time.Second,\n    }\n    goptions.ParseAndFail(\u0026options)\n}\n\n```\n\n[更多列子](https://github.com/voxelbrain/goptions)\n\n\n- zmq\n\nserver\n```go\n\npackage main\n\nimport (\n    zmq \"github.com/pebbe/zmq4\"\n    \"github.com/pebbe/zmq4/examples/kvsimple\"\n\n    \"fmt\"\n    \"math/rand\"\n    \"time\"\n)\n\nfunc main() {\n    //  Prepare our context and publisher socket\n    publisher, _ := zmq.NewSocket(zmq.PUB)\n    publisher.Bind(\"tcp://*:5556\")\n    time.Sleep(200 * time.Millisecond)\n\n    kvmap := make(map[string]*kvsimple.Kvmsg)\n    rand.Seed(time.Now().UnixNano())\n\n    sequence := int64(1)\n    for ; true; sequence++ {\n        //  Distribute as key-value message\n        kvmsg := kvsimple.NewKvmsg(sequence)\n        kvmsg.SetKey(fmt.Sprint(rand.Intn(10000)))\n        kvmsg.SetBody(fmt.Sprint(rand.Intn(1000000)))\n        err := kvmsg.Send(publisher)\n        kvmsg.Store(kvmap)\n        if err != nil {\n            break\n        }\n    }\n    fmt.Printf(\"Interrupted\\n%d messages out\\n\", sequence)\n}\n\n```\n\nclient\n\n```go\n\npackage main\n\nimport (\n    zmq \"github.com/pebbe/zmq4\"\n    \"github.com/pebbe/zmq4/examples/kvsimple\"\n\n    \"fmt\"\n)\n\nfunc main() {\n    //  Prepare our context and updates socket\n    fmt.Println(\"zmq test\")\n    updates, _ := zmq.NewSocket(zmq.SUB)\n    updates.SetSubscribe(\"\")\n    updates.Connect(\"tcp://localhost:5556\")\n\n    kvmap := make(map[string]*kvsimple.Kvmsg)\n\n    sequence := int64(0)\n    for ; true; sequence++ {\n        kvmsg, err := kvsimple.RecvKvmsg(updates)\n        if err != nil {\n            break //  Interrupted\n        }\n        fmt.Println(kvmsg.GetBody())\n        kvmsg.Store(kvmap)\n    }\n    fmt.Printf(\"Interrupted\\n%d messages in\\n\", sequence)\n}\n\n\n```\n\n    \n    \n    \n# 第四章 程序组织与自定义包编写\n\n到此为之，都是在涂鸦式的玩具编程，真正开始做起项目来，是需要对程序进行组织的。比如你可能定义一些功能函数，定义一些模块，这时需要自己进行一些的封装。\ngo中并不存在传统上先include文件，然后引用的功能。任何定义好的模块都需要封装成包以后才能复用。go强制要求这一点，初用可能很不习惯，但是慢慢就会体会到这里面的好处。\n\n好了，先把以前联系的代码都删了吧，让我们从真正的工程开始玩。\n\n进入GOPATH目录下，SRC目录下，建立一个新的文件夹：utility。也可以建立多层文件夹，这个随意。\n然后里面新建go文件，myFmt.go\n\n```go\n\npackage utility\n\nimport \"fmt\"\n\nfunc Print(str string)  {\n    fmt.Println(str)\n}\n\n\n```\n\n 回到GOPATH目录下，SRC目录下，新建一个文件夹： myProject\n 然后里面建立go文件，main.go\n ```go\n\npackage main\n\nimport (\n    \"../utility\" //相对用法不推荐\n    \"NodeHopeDaemon/utility\"// 推荐使用这种结构。这里面以GOPATH/SRC为基础目录的（概念上的）绝对路径\n)\nfunc main()  {\n    utility.Print(\"hello\")\n\n}\n\n 输出结果\n hello\n\n ```\n\n 例如我的目录结构为\n ![](img/package.png)\n\n 还记得第一章说的GOPATH的理解问题吗，在这里就需要更正了，GOPATH不仅仅只是用来安装包，更常规的概念是当作工作区来用。\n\n\n#第五章 编译\ngo的核心优势是跨平台,在不使用cgo的前提下, go的代码是可以直接编译出不同平台,不同系统的可执行文件的,而且支持交叉编译.\n\n设置平台(x86或者x64)\n```\nSET GOARCH=386\nSET GOARCH=amd64\n```\n\n设置系统(windows或者mac或者linux)\n```\nSET GOOS=windows\nSET GOOS=linux\nSET GOOS=linux\n```\n运行编译命令\n```\nGO build test.go\n```\n\n#第六章 数据类型转换\n\n数据类型转换经常用到，单独章节。\n\n##string 转换其他\n- float\n```\n        strconv.ParseFloat()\n```\n- int\n```\n        strconv.ParseInt()\n```\n- bool \n```\n        strconv.ParseBool()\n```\n        \n##int 转换其他\n- string\n```\n        // 通过Itoa方法转换  \n        str1 := strconv.Itoa(i)  \n        // 通过Sprintf方法转换  \n        str2 := fmt.Sprintf(\"%d\", i)  \n        // 通过FormatInt转换\n        str3 = strconv.FormatInt()\n```\n- float\n```\n        float(i)\n```\n- bool \n```\n        bool(i)\n```\n\n##float 转换其他\n- string\n```\n        // 通过Itoa方法转换  \n        str1 := strconv.Itof(f)  \n        // 通过Sprintf方法转换  \n        str2 := fmt.Sprintf(\"%f\", f)  \n        // 通过FormatInt转换\n        str3 = strconv.FormatFloat()\n```\n- int\n```\n        int(i)\n```\n- bool \n```\n        bool(i)\n```\n\n##bool 转换其他\n- string\n```\n        // 通过Sprintf方法转换  \n        str2 := fmt.Sprintf(\"%d\", b)  \n        // 通过FormatInt转换\n        str3 = strconv.FormatBool()\n```\n- int\n```\n        int(i)\n```\n- float \n```\n        float(i)\n```\n\n##单独说下byte\n- byte转换string\n```\n    string(byte)\n```\n- byte转换int、bool、float\n使用encoding/binary包做转换\n\n- int、bool、float转换byte\n使用encoding/binary包做转换\n\n# 第七章 使用cgo与跨平台\n\n## cgo的使用\n```\n/*\n#cgo CFLAGS : -I./c\n#cgo LDFLAGS: -ldl\n#include \"loader.c\"\n\n*/\nimport \"C\"\n```\n## cgo的数据类型\n- 普通数据类型：c中的数据类型直接对应，如c.int c.ulong c.char\n- 结构体：c中的struct XX 对应c.struct_XX\n- 空指针：unsafe.Pointer\n- 数组：pCmdBuf           [4096]C.uchar\n![](img/ctype.png)\n- 字符数组：\n    使用C.CString进行装换，但是要注意go的string是自动释放内存的，使用cgo需要手动释放。\n    ```\n    var LastValue string = \"你好\"\n    csLastValue := C.CString(LastValue)\n    defer C.free(unsafe.Pointer(csLastValue))\n\n    ```\n## xgo\n- docker\n    docker是非常先进的技术，如果还没使用过，快去尝试吧。\n- 使用xgo进行跨平台编译\n    go本身是支持跨平台编译的，但是如果使用了cgo，需要安装gcc跨平台编译器。\n    [xgo](https://github.com/karalabe/xgo)是封装好的docker镜像，内部继承了跨平台的编译环境。\n    具体命令详见官网，我这里举个简单的工程例子：\n    ![](img/docker.png)\n    我的编译命令为：\n```\ndocker run -it -v \"$PWD\"/build:/build -v \"$PWD\"/../:/go/src -v \"$PWD\":/go/src/NodeHopeIOAccessCross -e TARGETS=linux/arm-7 karalabe/xgo-latest NodeHopeIOAccessCross\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackhu1990%2Fgolangman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjackhu1990%2Fgolangman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackhu1990%2Fgolangman/lists"}