go学习

hangeqq685042 · · 1351 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

        对于go的学习,需要注意的地方。


        1、特别注意,go中可以使用go env命令查看我们可以在环境变量中设置的变量。

        2、一般的输出使用内建函数print或者println就可以了,fmt.printXXX函数有更多的作用,比如格式化输出。同时注意,内建的println并不是很好用,一般都是会在函数运行完之后才会被调用。

        3、注意,在go中,同时存在函数和方法两种概念,这点和java是不一样的。在go中,类的函数叫做方法,而不是类的函数就是函数。

        4、系统输出数组喜欢用[]并使用空格作为元素分隔,所以不要以为[]和空格是数据本来就又的,这点需要注意!

        5、java以文件学习,因为java中国一个文件对应一个类,而go以模块学习,因为go中都是直接调用模块的函数,而模块的函数都分布在模块下的文件里面。

        6、代码块中的:=短式声明在代码块外不起作用,另外,多使用go tool vet -shadow your_fil.go检查隐藏变量。

        7、GOBIN环境变量可以让go install的exe文件放置在GOBIN下。

        8、函数有多少个返回值,在给变量赋值的时候就需要有多少变量需要来获取这些返回值,但是如果使用range就可以只获取第一个返回值,这点在range可以操作channel就可以看出来了。

        9、使用go get url之后,使用import "url"就可以使用这个包了。

        10、可以再var()和const()中声明重复的变量,可以再函数中使用:=短式声明多个变量,其中有变量是与上面的变量重复的,这样也是允许的。但是需要注意,在匿名函数中重复声明匿名函数外的变量,并不会影响匿名函数外的变量,这点需要注意。

        11、一般使用组合来实现继承关系,所以可以很容易查找到继承的关系,也就可以更好的给函数传值了,一般不会要求通过看函数实现来定位继承关系。

        12、实例化可以分为有序初始化和无序初始化。无序初始化:数组{1:“aaa”,3:“bbb”},类xxx{name1:“ddd”,name2:234}

        13、变量不可以使用iota,因为常量如果没有被赋值,他会跟上一个常量一个值,而变量不会。

        14、需要特别注意,只有append()函数可以给slice增加容量,不要以为使用超出slice的index的XXX[index]就可以给slice增加容量,这是错误的,需要注意。另外,append函数给slice增加元素时,如果元素少于1000时,容量按照2倍增加,多于1000时1.5倍增加。

        15、使用os.OpenFile设置打开的时候清空目标文件的数据,或者使用File.Truncate函数清除数据都可以用于清楚数据,这里需要注意的是go的清除数据函数使用truncate命名而不是clean或者clear,这是因为truncate可以清除部分数据,这样更加简洁了。

        16、go中正则使用regexp模块,使用regexp.complie函数返回一个Regexp对象,使用Regexp.FindXXX函数查找,最后使用FindSubmatch函数可以查找完整匹配和子项匹配,这点跟java的group(n)一样。另外,使用fmt.Printf("%q", )可以格式化输出,特别是像regexp.Split函数返回的[][]byte数组,更加应该通过fmt.Printf("%q", )输出,不然输出的是数字。

        17、注意,go中是根据属性来放置容器类的,所以container模块中没有多少文件,而io模块中有pipe文件

        18、io.WriteString和ioutil.WriteFile形成的文件是文本格式的,而文本格式会添加东西,所以又出错的可能,而io.Copy函数则不会,因为是根据字节复制的。

        19、go的编码包中,一般字符串的编码解码使用Marshal和Unmarshal函数,对于流式读写则使用NewEncoder和NewDecoder函数生成需要的编码解码类,比如对xml,json,gob操作。最后需要注意,go的编码都是utf-8的,所以需要查看操作的编码格式,如果在网页中的话,可以在header的meta中查看charset就能知道编码格式了。

        20、windres.exe可以给go的exe程序打包图标,windres.exe在MinGW,tdm-gcc中都可以找到。

        21、特别注意,switch后面的表达式可以是任意类型,不限于常量,同时可以先使用初始化语句然后使用“;”分隔后面的表达式。case后面的表达式可以是参数列表例如1,2,3,甚至是值,例如arr[1]。另外,别忘了还可以使用default。

        22、在go中,使用T.(X)这种方式实现类型断言,其中T必须是interface{},而X可以是任意类型,甚至世界史type关键字也是可以的,只是如果是type关键字的话就只能在switch中使用了。最后注意,使用断言还可以实现类型转换,就是将interface{}转换为需要的类型,就是X,Ok := T.(X),其中T是interface{},X是需要转化的类型。

        23、可以将函数名作为参数传递给函数,这点跟python一样。例如定义一个函数X(),然后定义一个函数XX(src func()){src()},然后调用XX(X)。

        24、go中应该注意,对于变量,应该分为声明和初始化,生命在左边,而初始化在右边。完整的变量声明与初始化时左右两边都有声明的,左边声明变量类型,而右边声明初始化的对象类型,=号用于赋值。也就是说完整的表达式是左右两边都有声明。但是基本数据类型可以只写左边声明,或者只写右边声明,甚至使用:=短式,完全不写声明。但是如果是复合类型,那么就只能是使用:=短式和右边声明。也就是说初始化的复合类型对象的类型是必须要写的,例如var xx = []string{}可以,但是var xx []string = {"d", 'ss"}却不行。简单点说其实就是复合类型已经是类了,系统无法自行辨认。

        25、特别注意,在使用数组声明并初始化时,需要写明数组的个数,声明和初始化都要。如果少写了,虽然在编码时不会报错,但是编译会出错!

        26、特别注意,在go中是以模块作为import的,所以不能出现模块环引用,这会报错,避免的方法是使用传递参数,传递函数和使用接口代替









        在go中调用C/C++是非常容易的,可以分为两种:1、可以直接嵌入。在import “C”上使用//或者/**/然后再里面添加需要的例如#include xxx或者代码。这里还可以使用#cgo操作cgo命令,例如#cgo LDFLAGS : -L ./ -lfoo告诉编译器,链接当前目录下lfoo共享库,也就是so文件,如果在编译中使用so库,使用命令go build -ldflags="-r DIR",其中DIR是so库目录。2、在src下创建一个专门存放c代码的文件夹,然后在里面添加h,c,go文件,go文件用于将c,h包装给外面使用。例如下面的例子:


fun.c:

#include <stdio.h>
#include <stdlib.h>

char* MySecret() {
  return "my secret not tell anybody";
}


fun.h:

extern char* MySecret();


fun.go


package misc


//注意,这里可以使用#include dir来引进某个c库,实际上include就是为了引进文件
//C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt下有我们需要
//的如stdlib.h等文件


// #include "fun.h"
import "C"

func MySecret() string {
    return (C.GoString(C.MySecret()));
}



        网络编程大致上分为b/s,c/s两种。而go中,net实现底层网络功能,net.http实现高级的网络功能。net模块一般用于c/s,而net.http则是c/s,b/s都可以。但是无论是哪种实现方式,其实都是客户端请求数据,而服务端监听并给予数据。

        在go中使用net.Dial函数就可以实现客户端的socket功能了,在服务端有net.Listen函数作为服务端的socket功能。这是go的net底层的功能,也是实现c/s的基础方式。

        对于常见的方式,应该是使用net.http模块的功能。一个url对应一个网络资源,常见的Get,Post,Put,Delete分别对应增删查改,get查询,post修改,put增加,delete删除。网页的获取方式是get,所以一般get不会被拒绝。这里客户端一般使用net.http.Get()或者net.http.Post()进行数据获取,而服务端使用net.http.ListenAndServe()监听并操作数据,如果是浏览器,其实也是通过net.http.Get()获取数据的,net.http.Post()交互数据的。



        对于go的包,不要命名为main,all,std等,不要再src下创建main包,因为在src下创建一个文件并添加package main和main函数就可以作为程序的入口文件了。在引用自己项目的东西的时候,需要使用import "./xxx"而不能是直接import  "xxx",不然会在编译的时候出错。这样做其实是为了不和go系统下的文件混淆,当然,如果直接将项目放到GOPATH下的第一个路径,那么可以直接使用import  "xxx",因为这个时候,项目被当成系统文件了。

        另外,自己的项目模块的命名最好不要跟系统的模块名字一样,因为系统分辨不出来。

        最后需要注意,在go项目下点号"."代表的是当前项目路径。

        使用go get命令下载并编译的文件会放在GOPATH的第一个路径下,另外,我们的项目也是需要添加到GOPATH下的。最后注意,项目定位到项目名,而不是工作空间名。另外注意,对于需要翻墙的,如果使用cmd,putty,一般网上的代理配置完之后都没用,可以使用git clone直接下载需要翻墙的部分,当然git需要配置代理。然后使用go get下载不用翻墙的部分。其实go get就是使用git clone之后编译下载的项目而已。

        当然我们可以使用gopm这个包管理工具,这个工具可以用于安装,搜索,更新和分享go包,所以对于go的第三方包的管理,使用gopm是个不错的选择。



        在go中只有不可修复的错误才使用defer,panic(),recover(),其他的用error,一般使用errors.New,fmt.Errorf函数都可以创建实现error接口的错误对象。这里注意,errors本身就是用于操作error的,而fmt则是格式化错误输出。

        应该说defer只是延迟操作而已,真正的错误操作是panic和recover函数。其中,panic是保存一个异常信息,因此多次调用panic,当前异常会覆盖上一个异常。其实recover可以在任何地方调用,只是如果没有panic的错误,那么返回的是nil,所以recover只有在defer中调用才能得到异常信息了,而且panic之后程序就返回了。最后需要特别注意,recover需要在defer中直接调用,或者defer后的函数或者匿名函数里面直接调用才可以。不然返回nil,这点可以在进程工作空间看出。



        go中有errors,bytes,strings三个模块分别对应error,byte,string的操作封装,应该说go很喜欢使用XXXs来封装对XXX的操作这种命名模式。当然了,XXutil这种也很常见,比如ioutil模块用于辅助io操作。另外,io模块仅仅是常规的io类创建和操作而已,而bufio模块则封装了更多的io操作,特别是返回数据方面,io模块都是字节数组操作的,很不方便,而bufio有更多的操作给我们使用。最后,bufio模块中的scan模块是用于切分的,关键是里面的Scaner类,传参数给Scaner.Split函数,然后调用Scaner.Scan函数产生一次读取扫描,然后可以进行更多的操作。


        go中,string和[]byte其实是一样的,只是被定义成了不同的类型,并且string拥有了函数。所以bytes,strings两个模块的函数很多很相似,比如两个模块都有去空格函数。应该特别注意,java中String封装的很多函数,在go中就在strings模块中。同样的bytes封装了[]byte的操作。同时注意,封装了很多很好用的正则函数。最后注意,bytes,strings都封装了相应的Reader,Writer类,和NewReader,NewWriter函数,这也是为了读写操作封装的。

        Reader里面的数据读取一次之后就没有了,所以我们可以使用ioutil.FindAll函数得到字节数组,然后将字节数组传递给bytes.NewReader生成Reader,这样就可以再次操作Reader了。

        需要注意,bytes.NewBufferString跟java中的StringBuffer一个道理,当然可以使用bytes的Buffer类进行读写。另外,使用fmt.Printf跟strings的操作速度一样,而+的操作更快,最快的是使用bytes.Buffer,bytes.NewBufferString的操作。



        go中,最应该掌握的是builtin模块,这个模块里面的都是内建函数,这个模块里面的函数都是用于操作内建数据类型的。比如len函数就是用于得到内建数据类型的数据的长度的。另外,nil的==,!=比较只能是在内置函数中进行,这是因为在go中nil被定义了类型。最后注意,除了指针类型,其他的内置数据类型不能修改指针地址。

        go的内建函数copy用于数组间的赋值,可以将string编程[]byte,然后copy之后,将[]byte转化为string,这同时也是strings.Join函数的原理。

        说到go的类型,就需要注意type,type用于定义而不是用于别名,type不仅仅可以定义struct,interface,func,还可以用在基本数据类型上。另外,struct用{}初始化,func用()初始化,而interface自身是函数集合。另外,可以在type一个func之后给这个func添加成员func。最后注意,可以type一个func赋值给type的interface,只需要两者有共同的函数,这个过程其实就是type一个func作为函数集合,简单点说就是直接实现函数集合,这比创建一个struct去实例化interface要简单得多。例子如下:

type TestInterface interface {Do()}
type FuncDo func()
func (self FuncDo) Do() {self()}
func main() {
 var t TestInterface = FuncDo(func() {println("hello world")})
t.Do()
 }



        对于go的第三方包goquery,提供了方便的操作DOM的API,在分析html中非常有用。goquery借鉴了jquery。注意:jquery可以给网页插入元素,添加效果,所以可以不用熟悉js,而通过jquery的脚本编程给网页添加代码,效果。



        需要特别注意区分cmd.Args和os.Args,其中,其中cmd.Args是我们去调用命令的时候使用的,而os.Args则是系统调用我们编码的命令程序的时候使用的,所以os.Args[0]可以看到调用的路径,而cmd.Args就是命令的名字,但其实只是os.Args[0]看到的是完整的命令的路径而已,其实如果是我们调用程序,那么os.Args[0]只是输入的第一个字符串而已,系统调用才是完整路径。所以如果是go run就会与路径。os命令行比较麻烦,可以使用flag模块,flag可以直接使用类似go run XX.go -x -xx模式,其中x,xx是自定义参数,同时注意可以使用可以使单横杠“-”,也可以是双横杠“--”。



        在go中对于数据类型是需要非常注意的地方,在java中数据类型转换很方便,但是在go中,数据转换不支持隐式转换,所有的转换都需要显式转换。

        注意,xxx()可以用于基本数据类型的转换,但是跟string的转换就不行了,当然由于go中string的本质是int的,所以其实string(int)还是可以的。所以关于string跟其他类型的转换需要使用strconv模块进行。总的来说就是,字符串的转换需要使用strconv模块,其他的基本数据类型之间的转换使用XXX()就可以了。

        另外注意,查看数据的类型有两种方式:

1、

func t(i interface{}) {    //函数t 有一个参数i
    switch i.(type) {      //多选语句switch
    case string:
        //是字符时做的事情
    case int:
        //是整数时做的事情
    }
    return
}
i.(type)只能在switch中使用,所以这种方式局限性比较大。

2、使用Reflect.TypeOf()函数进行判断






未完成







有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:hangeqq685042

查看原文:go学习

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1351 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传