golang小程序试验(四)
壬癸甲乙 · · 3585 次点击 · · 开始浏览1. golang的函数类型转换
一个go playground的例子,首先定义一个func类型的别名A,然后定义了A的一个方法。再定义一个函数,此函数的参数与返回值与A相同(这样才能显式转换)。在main中把此函数显式转换为A类型,这样它就可以调用A的方法了。
package main
import "fmt"
type A func(int, int)
func (f A)Serve() {
fmt.Println("serve2")
}
func serve(int,int) {
fmt.Println("serve1")
}
func main() {
a := A(serve)
a(1,2)
a.Serve()
}
2. golang的http包处理流程
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
func main() {
http.HandleFunc("/hello", HelloServer)
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
首先调用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的闭包一例
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
} 对于以上代码的正确结果,需要理解闭包中的全局和局部变量,sum是全局变量,x是局部变量,这样就不会错了。
运行结果:
0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90
4. 获取golang的各种路径
用户当前所在路径:
os.Getwd()
执行程序文件相对路径:
file, _ := exec.LookPath(os.Args[0])
package main
import(
"os"
"log"
"os/exec"
"path"
)
func main() {
file, _ := os.Getwd()
log.Println("current path:", file)
file, _ = exec.LookPath(os.Args[0])
log.Println("exec path:", file)
dir,_ := path.Split(file)
log.Println("exec folder relative path:", dir)
os.Chdir(dir)
wd, _ := os.Getwd()
log.Println("exec folder absolute path:", wd)
}
可执行文件我放在/home/lm/handcode/test
我执行的路径是/home/lm/
[lm@openstack ~]$ handcode/test 2013年02月06日 11:09:07 current path: /home/lm 2013年02月06日 11:09:07 exec path: handcode/test 2013年02月06日 11:09:07 exec folder relative path: handcode/ 2013年02月06日 11:09:07 exec folder absolute path: /home/lm/handcode
5. golang的Error()
可以为自定义对象定义自己的Error()、String()方法,从而输出指定信息:
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return "cannot Sqrt negative number:" + fmt.Sprint(float64(e))
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, ErrNegativeSqrt(f)
}
z := float64(1)
for {
y := z - (z*z-f)/(2*z)
if math.Abs(y-z) < 1e-10 {
return y, nil
}
z = y
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
} 当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包没有被正确导入时,会提示如下错误:
[root@localhost src]# go build main.go main.go:5:5: cannot find package "fsnotify" in any of: /software/go/src/pkg/fsnotify (from $GOROOT) /software/fsnotify/src/fsnotify (from $GOPATH)也就是说在main.go中的import "fsnotify"首先根据$GOROOT找,如果找不到再根据$GOPATH找,还找不到的话就提示以上错误。先执行go env看一下go对应的环境变量:
[root@localhost software]# go env GOARCH="amd64" GOBIN="" GOCHAR="6" GOEXE="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOPATH="/software/fsnotify" GORACE="" GOROOT="/software/go" GOTOOLDIR="/software/go/pkg/tool/linux_amd64" TERM="dumb" CC="gcc" GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread" CXX="g++" CGO_ENABLED="1"
所以正确的做法是在$GOROOT/src/pkg下建立一个与自定义包名相同的文件夹,然后把相应文件放到这个文件夹下;或者在$GOPATH/src下建立一个与自定义包名相同的文件夹,把相应文件放进去,就OK了。一般情况下都是使用第二种做法。
$GOPATH可以定义多个路径,与$PATH的道理一样,可以在/root/.bashrc中这样定义$GOPATH:
GOPATH=/software/fsnotify:/software/tmp export GOPATH
然后把之前的fsnotify文件夹mv到/software/tmp/src下,go build main.go依然成功,以上两点得到验证。
7. golang的项目目录结构
一般的,一个go项目在$GOPATH下会有如下3个目录:
——bin ——pkg ——src其中,bin存放编译后的可执行文件;pkg存放编译后的包文件;src存放项目源文件。一般bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。对于pkg目录,pkg中的文件是Go编译生成的,而不是手动放进去的。(一般文件后缀.a)对于src目录,用来存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。
比如新建一个名为test的go项目,初始目录如下:
test/ |——install |——srcinstall内容如下:
#!/usr/bin/env bash if [ ! -f install ]; then echo 'install must be run within its container folder' 1>&2 exit 1 fi CURDIR=`pwd` OLDGOPATH="$GOPATH" export GOPATH="$CURDIR" gofmt -w src go install test export GOPATH="$OLDGOPATH" echo 'finished'
之所以加上这个install,是不用配置GOPATH(避免新增一个GO项目就要往GOPATH中增加一个路径)
接下来,增加一个包:config和一个main程序。目录结构如下:
test |-- install `-- src |-- config | `-- config.go `-- test `-- main.go
注意config.go中的package名称最好和目录config一致,而文件名可以随便。main.go表示main包,文件名建议为main.go。(注:不一致时,生成的.a文件名和目录名一致,这样,在import 时,应该是目录名,而引用包时,需要包名。例如:目录为myconfig,包名为config,则生产的静态包文件是:myconfig.a,引用该包:import "myconfig",使用包中成员:config.LoadConfig())
config.go代码:
package config
func LoadConfig() {
} main.go代码:
package main
import (
"config"
"fmt"
)
func main() {
config.LoadConfig()
fmt.Println("Hello, GO!")
}
在项目根目录执行./install
这时候的目录结构为:
test |-- bin | `-- test |-- install |-- pkg | `-- linux_amd64 | `-- config.a `-- src |-- config | `-- config.go `-- test `-- 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大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
1. golang的函数类型转换
一个go playground的例子,首先定义一个func类型的别名A,然后定义了A的一个方法。再定义一个函数,此函数的参数与返回值与A相同(这样才能显式转换)。在main中把此函数显式转换为A类型,这样它就可以调用A的方法了。
package main
import "fmt"
type A func(int, int)
func (f A)Serve() {
fmt.Println("serve2")
}
func serve(int,int) {
fmt.Println("serve1")
}
func main() {
a := A(serve)
a(1,2)
a.Serve()
}
2. golang的http包处理流程
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
func main() {
http.HandleFunc("/hello", HelloServer)
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
首先调用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的闭包一例
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
} 对于以上代码的正确结果,需要理解闭包中的全局和局部变量,sum是全局变量,x是局部变量,这样就不会错了。
运行结果:
0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90
4. 获取golang的各种路径
用户当前所在路径:
os.Getwd()
执行程序文件相对路径:
file, _ := exec.LookPath(os.Args[0])
package main
import(
"os"
"log"
"os/exec"
"path"
)
func main() {
file, _ := os.Getwd()
log.Println("current path:", file)
file, _ = exec.LookPath(os.Args[0])
log.Println("exec path:", file)
dir,_ := path.Split(file)
log.Println("exec folder relative path:", dir)
os.Chdir(dir)
wd, _ := os.Getwd()
log.Println("exec folder absolute path:", wd)
}
可执行文件我放在/home/lm/handcode/test
我执行的路径是/home/lm/
[lm@openstack ~]$ handcode/test 2013年02月06日 11:09:07 current path: /home/lm 2013年02月06日 11:09:07 exec path: handcode/test 2013年02月06日 11:09:07 exec folder relative path: handcode/ 2013年02月06日 11:09:07 exec folder absolute path: /home/lm/handcode
5. golang的Error()
可以为自定义对象定义自己的Error()、String()方法,从而输出指定信息:
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return "cannot Sqrt negative number:" + fmt.Sprint(float64(e))
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, ErrNegativeSqrt(f)
}
z := float64(1)
for {
y := z - (z*z-f)/(2*z)
if math.Abs(y-z) < 1e-10 {
return y, nil
}
z = y
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
} 当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包没有被正确导入时,会提示如下错误:
[root@localhost src]# go build main.go main.go:5:5: cannot find package "fsnotify" in any of: /software/go/src/pkg/fsnotify (from $GOROOT) /software/fsnotify/src/fsnotify (from $GOPATH)也就是说在main.go中的import "fsnotify"首先根据$GOROOT找,如果找不到再根据$GOPATH找,还找不到的话就提示以上错误。先执行go env看一下go对应的环境变量:
[root@localhost software]# go env GOARCH="amd64" GOBIN="" GOCHAR="6" GOEXE="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOPATH="/software/fsnotify" GORACE="" GOROOT="/software/go" GOTOOLDIR="/software/go/pkg/tool/linux_amd64" TERM="dumb" CC="gcc" GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread" CXX="g++" CGO_ENABLED="1"
所以正确的做法是在$GOROOT/src/pkg下建立一个与自定义包名相同的文件夹,然后把相应文件放到这个文件夹下;或者在$GOPATH/src下建立一个与自定义包名相同的文件夹,把相应文件放进去,就OK了。一般情况下都是使用第二种做法。
$GOPATH可以定义多个路径,与$PATH的道理一样,可以在/root/.bashrc中这样定义$GOPATH:
GOPATH=/software/fsnotify:/software/tmp export GOPATH
然后把之前的fsnotify文件夹mv到/software/tmp/src下,go build main.go依然成功,以上两点得到验证。
7. golang的项目目录结构
一般的,一个go项目在$GOPATH下会有如下3个目录:
——bin ——pkg ——src其中,bin存放编译后的可执行文件;pkg存放编译后的包文件;src存放项目源文件。一般bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。对于pkg目录,pkg中的文件是Go编译生成的,而不是手动放进去的。(一般文件后缀.a)对于src目录,用来存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。
比如新建一个名为test的go项目,初始目录如下:
test/ |——install |——srcinstall内容如下:
#!/usr/bin/env bash if [ ! -f install ]; then echo 'install must be run within its container folder' 1>&2 exit 1 fi CURDIR=`pwd` OLDGOPATH="$GOPATH" export GOPATH="$CURDIR" gofmt -w src go install test export GOPATH="$OLDGOPATH" echo 'finished'
之所以加上这个install,是不用配置GOPATH(避免新增一个GO项目就要往GOPATH中增加一个路径)
接下来,增加一个包:config和一个main程序。目录结构如下:
test |-- install `-- src |-- config | `-- config.go `-- test `-- main.go
注意config.go中的package名称最好和目录config一致,而文件名可以随便。main.go表示main包,文件名建议为main.go。(注:不一致时,生成的.a文件名和目录名一致,这样,在import 时,应该是目录名,而引用包时,需要包名。例如:目录为myconfig,包名为config,则生产的静态包文件是:myconfig.a,引用该包:import "myconfig",使用包中成员:config.LoadConfig())
config.go代码:
package config
func LoadConfig() {
} main.go代码:
package main
import (
"config"
"fmt"
)
func main() {
config.LoadConfig()
fmt.Println("Hello, GO!")
}
在项目根目录执行./install
这时候的目录结构为:
test |-- bin | `-- test |-- install |-- pkg | `-- linux_amd64 | `-- config.a `-- src |-- config | `-- config.go `-- test `-- 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接项目目录名。