golang小程序试验(四)

前端之家收集整理的这篇文章主要介绍了golang小程序试验(四)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1. golang的函数类型转换

一个go playground的例子,首先定义一个func类型的别名A,然后定义了A的一个方法。再定义一个函数,此函数的参数与返回值与A相同(这样才能显式转换)。在main中把此函数显式转换为A类型,这样它就可以调用A的方法了。

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type A func(int,int)
  6.  
  7. func (f A)Serve() {
  8. fmt.Println("serve2")
  9. }
  10.  
  11. func serve(int,int) {
  12. fmt.Println("serve1")
  13. }
  14.  
  15. func main() {
  16. a := A(serve)
  17. a(1,2)
  18. a.Serve()
  19. }

2. golang的http包处理流程

  1. func HelloServer(w http.ResponseWriter,req *http.Request) {
  2. io.WriteString(w,"hello,world!\n")
  3. }
  4.  
  5. func main() {
  6. http.HandleFunc("/hello",HelloServer)
  7. err := http.ListenAndServe(":12345",nil)
  8. if err != nil {
  9. log.Fatal("ListenAndServe: ",err)
  10. }
  11.  
  12. }

首先调用Http.HandleFunc

按顺序做了几件事:

1 调用了DefaultServerMux的HandleFunc

2 调用了DefaultServerMux的Handle

3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则

其次调用http.ListenAndServe(":12345",nil)

按顺序做了几件事情:

1 实例化Server

2 调用Server的ListenAndServe()

3 调用net.Listen("tcp",addr)监听端口

4 启动一个for循环,在循环体中Accept请求

5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()

6 读取每个请求的内容w,err := c.readRequest()

7 判断header是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux

8 调用handler的ServeHttp

9 在这个例子中,下面就进入到DefaultServerMux.ServeHttp

10 根据request选择handler,并且进入到这个handler的ServeHTTP

mux.handler(r).ServeHTTP(w,r)

11 选择handler:

A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)

B 如果有路由满足,调用这个路由handler的ServeHttp

C 如果没有路由满足,调用NotFoundHandler的ServeHttp

可以看一下这篇文章的介绍:http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html

3. golang的闭包一例

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func adder() func(int) int {
  6. sum := 0
  7. return func(x int) int {
  8. sum += x
  9. return sum
  10. }
  11. }
  12.  
  13. func main() {
  14. pos,neg := adder(),adder()
  15. for i := 0; i < 10; i++ {
  16. fmt.Println(
  17. pos(i),neg(-2*i),)
  18. }
  19. }
对于以上代码的正确结果,需要理解闭包中的全局和局部变量,sum是全局变量,x是局部变量,这样就不会错了。

运行结果:

  1. 0 0
  2. 1 -2
  3. 3 -6
  4. 6 -12
  5. 10 -20
  6. 15 -30
  7. 21 -42
  8. 28 -56
  9. 36 -72
  10. 45 -90

4. 获取golang的各种路径

用户当前所在路径:

os.Getwd()

执行程序文件相对路径:

file,_ := exec.LookPath(os.Args[0])

  1. package main
  2. import(
  3. "os"
  4. "log"
  5. "os/exec"
  6. "path"
  7. )
  8. func main() {
  9. file,_ := os.Getwd()
  10. log.Println("current path:",file)
  11. file,_ = exec.LookPath(os.Args[0])
  12. log.Println("exec path:",file)
  13. dir,_ := path.Split(file)
  14. log.Println("exec folder relative path:",dir)
  15. os.Chdir(dir)
  16. wd,_ := os.Getwd()
  17. log.Println("exec folder absolute path:",wd)
  18. }

可执行文件我放在/home/lm/handcode/test

我执行的路径是/home/lm/

  1. [lm@openstack ~]$ handcode/test
  2. 2013/02/06 11:09:07 current path: /home/lm
  3. 2013/02/06 11:09:07 exec path: handcode/test
  4. 2013/02/06 11:09:07 exec folder relative path: handcode/
  5. 2013/02/06 11:09:07 exec folder absolute path: /home/lm/handcode

5. golang的Error()

可以为自定义对象定义自己的Error()、String()方法,从而输出指定信息:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "math"
  6. )
  7.  
  8. type ErrNegativeSqrt float64
  9.  
  10. func (e ErrNegativeSqrt) Error() string {
  11. return "cannot Sqrt negative number:" + fmt.Sprint(float64(e))
  12. }
  13.  
  14. func Sqrt(f float64) (float64,error) {
  15. if f < 0 {
  16. return 0,ErrNegativeSqrt(f)
  17. }
  18.  
  19. z := float64(1)
  20. for {
  21. y := z - (z*z-f)/(2*z)
  22. if math.Abs(y-z) < 1e-10 {
  23. return y,nil
  24. }
  25. z = y
  26. }
  27. return z,nil
  28. }
  29.  
  30. func main() {
  31. fmt.Println(Sqrt(2))
  32. fmt.Println(Sqrt(-2))
  33. }
当f<0时把f显式转换成 ErrNegativeSqrt类型,传入到error,就会调用自定义的ErrNegativeSqrt的Error()方法

6. golang导入自己的package

以前写的go程序都是在main包中,即使有多个go文件,其开头都是package main,所以它们都属于main包。今天试验了一下其它自定一包的导入:定义了fsnotify.go和fsnotify_linux.go,这两个文件属于package fsnotify;定义main.go,属于package main。main.go会调用fsnotify包中的方法,那么怎样导入fsnotify包呢?

在fsnotify包没有被正确导入时,会提示如下错误

  1. [root@localhost src]# go build main.go
  2. main.go:5:5: cannot find package "fsnotify" in any of:
  3. /software/go/src/pkg/fsnotify (from $GOROOT)
  4. /software/fsnotify/src/fsnotify (from $GOPATH)
也就是说在main.go中的import "fsnotify"首先根据$GOROOT找,如果找不到再根据$GOPATH找,还找不到的话就提示以上错误。先执行go env看一下go对应的环境变量:
  1. [root@localhost software]# go env
  2. GOARCH="amd64"
  3. GOBIN=""
  4. GOCHAR="6"
  5. GOEXE=""
  6. GOHOSTARCH="amd64"
  7. GOHOSTOS="linux"
  8. GOOS="linux"
  9. GOPATH="/software/fsnotify"
  10. GORACE=""
  11. GOROOT="/software/go"
  12. GOTOOLDIR="/software/go/pkg/tool/linux_amd64"
  13. TERM="dumb"
  14. CC="gcc"
  15. GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread"
  16. CXX="g++"
  17. CGO_ENABLED="1"

所以正确的做法是在$GOROOT/src/pkg下建立一个与自定义包名相同的文件夹,然后把相应文件放到这个文件夹下;或者在$GOPATH/src下建立一个与自定义包名相同的文件夹,把相应文件放进去,就OK了。一般情况下都是使用第二种做法。

$GOPATH可以定义多个路径,与$PATH的道理一样,可以在/root/.bashrc中这样定义$GOPATH:

  1. GOPATH=/software/fsnotify:/software/tmp
  2. export GOPATH

然后把之前的fsnotify文件夹mv到/software/tmp/src下,go build main.go依然成功,以上两点得到验证。

7. golang的项目目录结构

一般的,一个go项目在$GOPATH下会有如下3个目录:

  1. ——bin
  2. ——pkg
  3. ——src
其中,bin存放编译后的可执行文件;pkg存放编译后的包文件;src存放项目源文件。一般bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。对于pkg目录,pkg中的文件是Go编译生成的,而不是手动放进去的。(一般文件后缀.a)对于src目录,用来存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。

比如新建一个名为test的go项目,初始目录如下:

  1. test/
  2. |——install
  3. |——src
install内容如下:
  1. #!/usr/bin/env bash
  2.  
  3. if [ ! -f install ]; then
  4. echo 'install must be run within its container folder' 1>&2
  5. exit 1
  6. fi
  7.  
  8. CURDIR=`pwd`
  9. OLDGOPATH="$GOPATH"
  10. export GOPATH="$CURDIR"
  11.  
  12. gofmt -w src
  13.  
  14. go install test
  15.  
  16. export GOPATH="$OLDGOPATH"
  17.  
  18. echo 'finished'

之所以加上这个install,是不用配置GOPATH(避免新增一个GO项目就要往GOPATH中增加一个路径)

接下来,增加一个包:config和一个main程序。目录结构如下:

  1. test
  2. |-- install
  3. `-- src
  4. |-- config
  5. | `-- config.go
  6. `-- test
  7. `-- main.go

注意config.go中的package名称最好和目录config一致,而文件名可以随便。main.go表示main包,文件名建议为main.go。(注:不一致时,生成的.a文件名和目录名一致,这样,在import 时,应该是目录名,而引用包时,需要包名。例如:目录为myconfig,包名为config,则生产的静态包文件是:myconfig.a,引用该包:import “myconfig”,使用包中成员:config.LoadConfig())

config.go代码

  1. package config
  2.  
  3. func LoadConfig() {
  4.  
  5. }
main.go代码
  1. package main
  2.  
  3. import (
  4. "config"
  5. "fmt"
  6. )
  7.  
  8. func main() {
  9. config.LoadConfig()
  10. fmt.Println("Hello,GO!")
  11. }

在项目根目录执行./install

这时候的目录结构为:

  1. test
  2. |-- bin
  3. | `-- test
  4. |-- install
  5. |-- pkg
  6. | `-- linux_amd64
  7. | `-- config.a
  8. `-- src
  9. |-- config
  10. | `-- config.go
  11. `-- test
  12. `-- main.go

其中config.a是包config编译后生成的;bin/test是生成的二进制文件

这个时候可以执行:bin/test了。会输出:Hello,GO!

包可以多层目录,比如:net/http包,表示源文件在src/net/http目录下面,不过源文件中的包名是最后一个目录的名字,如http

而在import包时,必须完整的路径,如:import “net/http”。

go build与go install的区别:

go build:在临时目录下创建包编译后的二进制文件,该命令不会将二进制文件安装到 bin、pkg目录;go build后面直接接需要编译的文件名。

go install:和 go build 参数相同,唯一的区别在于将编译结果拷贝到 bin、pkg目录中;go install需要建立一个与项目目录同名的子目录,然后把main.go放进去;go install接项目目录名。

猜你在找的Go相关文章