最近又打算玩玩golang,打算把工作中的爬虫从脚本语言迁移到golang,读了两本golang的书,对立面的语法做了一个简单的整理。
编程语言的选择
0 业务的要求 1 性能 2 表达能力 3 是否容易上手 4 坑和维护
变量
变量的声明 var xx type 或者 var (xx type xxx type)
初始化
var v1 i = 10; var v2 = 10; v3 := 10
交换值
i,j = j,i
多个值
_,a = 11,34
常量
const pi float32 = 1.2 const zero = 11 const( a1 int32 = 11; a2 = 11;) const a2 = 1 << 2
预定义常量
const( c0=iota;c1=iota;c2=iota;)//iota 值自动增加 const (a = 1 << iota; b ; c;)
枚举
const( day1=iota;day2;day3;day4;)
类型
bool var b1 bool = true; b2 := (1 == 2); int8,byte int16 int unit unitptr int32 unit32 int64 unit64 int unit 整型是不同的,数值运算,比较运算,位运算 float32 float64 var f1 float32 = 11.1; 不能直接比较大小,可以计算减法比较 complex64 complex128 com1 := 3+12i; 实部real(com1); 虚部imag(com1) string str1 := "hi"; len(str1);str1[0]; 字符串的运算 加 x+y 长度 len(s) 取字符 s[i] 字符串的遍历 for k,v := range str{ fmt.Println(k,v); } rune字符类型 byte,uint8的别名,一个是rune 代表unicode字符 unicode/utf8包提供utf-8和unicode的转换 指针 pointer 数组 array [32]int;[2*N]struct{x,y int32};[3][5]int 长度 alen := len(arr) 遍历 下标遍历,range遍历, 数组是值类型,传递是拷贝 切片 slice 创建切片 arr[a:b]; make([]int,len,cap); 遍历 小标遍历,range遍历 常用函数 len长度 cap容量 可以基于切片创建切片 新增元素 slice=append(slice,11) 内容复制 copy(slice1,slice2); 字典 map 创建字典 make(map[string]int,len) 赋值 map1[key] = val1 查找字典 p,ok := map1[key1] 删除 delete(map1,"k1") 通道 chan 结构体 struct 接口 interface error
流程语句
选择 if,else 和else if if xxx{ }else{ } switch switch xx{ case 1: fallthrough case 2: default: } select 循环 for for{} for a;b;c;{} break 跳出循环 continue 继续 range 循环 跳转 HERE: goto HERE
func (t *R) funcname(a type,b type)(ret type,err error){ return a,nil } 函数返回nil比较多 调用 a:= funcname(a,b); a:= FTT.funcname(a,b); 不定参数 func funcname2(arg ...int){ funcname3(arg...) } 任意参数 func funcname3(args ...interface{}) 匿名函数 func(x,y int)int{ return x+y } //调用匿名函数 func(x,y int)int{ print(x,y) }(1,2) 闭包: j :=3; func()(func()){ var i int = 1; return fun(){ fmt.Printf("i,j:%d,%d\n",i,j); } }()
获取数据的类型
xxx.(type)
nil
代表空
defer和panic 错误处理
error的定义 type error interface{ Error() string } defer 延后处理 例如defer func(){}()
panic() 和recover()
panic()释放错误 defer func(){ if r:= recover();r!=nil{ log.Printf("xxx"); } }()
import
相对路径 import "./xx" 绝对路径 import "xx/xx" 点导入 import . "math" 可以直接 sqrt() 别名 import f fmt 可以f.printf(xxx) _操作 _"github.com/xxx" 引用包不是直接调用函数,而是使用了包里面的init方法
go的类型系统
基础类型 byte int bool float 符合类型 数组,结构体,指针 任意类型 interface{} 值和引用语义 面向对象 接口 go 里面有引用语义的 切片,map,channel,接口
结构
type A struct{ stat int } func ( a A) add(a1 int,b1 int ) int{ return a1+b1 } 初始化 a1 := new(A) a2 := &A{} a3 := &A{1} a4 := &A{stat:1} 自建构造函数 func NewA(stat int){ return &A{stat} } 匿名组合 type B struct{ A } type B struct{ *A } 可见性 通过大小写控制
接口
type Read interface{ Reader(buf []byte)(n int,err error) } type FileReader struct{ int xx; } 实现接口 func (fr *FileReader)Reader(buf []byte)(n int,err error){ return 1,nil } 接口的赋值 对象赋值给接口通过引用 file_reader := new(FileReader); var b Read = file_reader 将接口赋值给接口 只需要一个是另外一个的子集即可,反过来不成立 IA > IB IA = IB 接口查询 检查是否实现了接口 if xx,ok := file_reader.(Read);ok{ } 接口组合 type interface1 interface{ read()(n int,err error) } type interface2 interface{ write()(n int,err error) } type interface3 interface{ interface1 interface2 }
任意类型
interface{}
类型查询
switch v:= v1.(type){ case xx1: case xx2: case xx3: }
并发
通过go实现 main方法调用go的方式 1 主线程睡眠 2 使用管道等待 3 for或者select死循环 管道的声明 ch := make(chan int) 管道写入数据 ch <- value 管道数据写入变量 value := <-ch select go在语言级别支持select select{ case <-chan1://chan1读取数据执行下面 case chan2 <- 1: chan2读取成功 default:默认流程 } 缓冲机制-自带线程池 c :=make(chan int,1023) for i:= range c{ printf("receiverd: ",i); } 超时机制 1 定义一个chan运行,到时间写即可,可以使用select来实现 2 也可以 使用 time.After(5.time.Second) 传递性 可以通过chan传递数据 type PipeData struct { value int handler func(int) int next chan int } func handle(queue chan *PipeData) { for data := range queue { data.next <- data.handler(data.value) } } 单向管道 读写 var ch1 chan int 写 var ch2 chan<- float32 读 var ch2 <-chan int 关闭管道 close(ch) 多核化 cpu个数的管道 Gosched让出时间片 同步 sync.Mutex sync.RWMutex 全局唯一操作 var once sync.Once once.Do(xxx) go并发相关函数: Goexit 退出去 Gosched 让出执行权限 Numcpu cpu核数量 NumGoroutine 执行和排队的携程数 GOMAXPROCES 核心数
反射
t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素 v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值 转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如 tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签 name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
package main import ( _ "github.com/Go-sql-Driver/MysqL" "database/sql" "fmt" //"time" ) func main() { db,err := sql.Open("MysqL","astaxie:astaxie@/test?charset=utf8") checkErr(err) //插入数据 stmt,err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?") checkErr(err) res,err := stmt.Exec("astaxie","研发部门","2012-12-09") checkErr(err) }
使用redis
package main import ( "github.com/astaxie/goredis" "fmt" ) func main() { var client goredis.Client client.Set("a",[]byte("hello")) val,_ := client.Get("a") fmt.Println(string(val)) client.Del("a") //list操作 var client goredis.Client vals := []string{"a","b","c","d","e"} for _,v := range vals { client.Rpush("l",[]byte(v)) } dbvals,_ := client.Lrange("l",4) for i,v := range dbvals { println(i,":",string(v)) } client.Del("l") }
操作xml
package main import ( "encoding/xml" "fmt" "io/IoUtil" "os" ) type Recurlyservers struct { XMLName xml.Name `xml:"servers"` Version string `xml:"version,attr"` Svs []server `xml:"server"` Description string `xml:",innerxml"` } type server struct { XMLName xml.Name `xml:"server"` ServerName string `xml:"serverName"` ServerIP string `xml:"serverIP"` } func main() { file,err := os.Open("servers.xml") // For read access. if err != nil { fmt.Printf("error: %v",err) return } defer file.Close() data,err := IoUtil.ReadAll(file) if err != nil { fmt.Printf("error: %v",err) return } v := Recurlyservers{} err = xml.Unmarshal(data,&v) if err != nil { fmt.Printf("error: %v",err) return } fmt.Println(v) }
正则表达式
func Match(pattern string,b []byte) (matched bool,error error) func MatchReader(pattern string,r io.RuneReader) (matched bool,error error) func MatchString(pattern string,s string) (matched bool,error error) 解析正则 func Compile(expr string) (*Regexp,error) func CompilePOSIX(expr string) (*Regexp,error) func MustCompile(str string) *Regexp func MustCompilePOSIX(str string) *Regexp 查找 func (re *Regexp) Find(b []byte) []byte func (re *Regexp) FindAll(b []byte,n int) [][]byte func (re *Regexp) FindAllIndex(b []byte,n int) [][]int func (re *Regexp) FindAllSubmatch(b []byte,n int) [][][]byte func (re *Regexp) FindAllSubmatchIndex(b []byte,n int) [][]int func (re *Regexp) FindIndex(b []byte) (loc []int) func (re *Regexp) FindSubmatch(b []byte) [][]byte func (re *Regexp) FindSubmatchIndex(b []byte) []int
模板处理
html/template"
文件处理
目录 文件操作的大多数函数都是在os包里面,下面列举了几个目录操作的: func Mkdir(name string,perm FileMode) error 创建名称为name的目录,权限设置是perm,例如0777 func MkdirAll(path string,perm FileMode) error 根据path创建多级子目录,例如astaxie/test1/test2。 func Remove(name string) error 删除名称为name的目录,当目录下有文件或者其他目录是会出错 func RemoveAll(path string) error 根据path删除多级子目录,如果path是单个名称,那么该目录不删除。 文件 func Create(name string) (file *File,err Error) 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写 func NewFile(fd uintptr,name string) *File 根据文件描述符创建相应的文件,返回一个文件对象 打开 func Open(name string) (file *File,err Error) 该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了OpenFile。 func OpenFile(name string,flag int,perm uint32) (file *File,err Error) 名字,方式,权限 写 func (file *File) Write(b []byte) (n int,err Error) 写入byte类型的信息到文件 func (file *File) WriteAt(b []byte,off int64) (n int,err Error) 在指定位置开始写入byte类型的信息 func (file *File) WriteString(s string) (ret int,err Error) 写入string信息到文件 读 func (file *File) Read(b []byte) (n int,err Error) func (file *File) ReadAt(b []byte,err Error)
字符串处理
字符串操作 func Contains(s,substr string) bool func Join(a []string,sep string) string func Index(s,sep string) int func Replace(s,old,new string,n int) string func Repeat(s string,count int) string func Split(s,sep string) []string func Trim(s string,cutset string) string func Fields(s string) []string 字符串转换 str := make([]byte,100) str = strconv.AppendInt(str,4567,10) str = strconv.AppendBool(str,false) str = strconv.AppendQuote(str,"abcdefg") str = strconv.AppendQuoteRune(str,'单') a := strconv.FormatBool(false) b := strconv.FormatFloat(123.23,'g',12,64) c := strconv.FormatInt(1234,10) d := strconv.FormatUint(12345,10) e := strconv.Itoa(1023) a,err := strconv.ParseBool("false") if err != nil {fmt.Println(err) } b,err := strconv.ParseFloat("123.23",64) if err != nil { fmt.Println(err) } c,err := strconv.ParseInt("1234",10,64) if err != nil { fmt.Println(err) } d,err := strconv.ParseUint("12345",64) if err != nil { fmt.Println(err) } e,err := strconv.Itoa("1023") if err != nil { fmt.Println(err) } fmt.Println(a,b,c,d,e) }
websocket
"code.google.com/p/go.net/websocket" 服务
gdb调试
日志系统
seelog
部署
daemon
性能监控
net/http/pprof runtime/pprof
常用的库
net包 socket 库 Dial(net,addr string)(Conn,error) conn,err := net.Dial("tcp","127.0.0.1:8888") conn,err := net.Dial("udp",err := net.Dial("ip4:icmp","www.baidu.com") conn,err := net.Dial("ip4:1","10.113.1.103") 目前支持的协议: tcp tcp4 tcp6 udp upd4 udp6 ip ip4 ip6 dial实际上是对函数的封装 func DialTCP(net string,laddr,raddr *TCPAddr) (c *TCPConn,err error) func DialUDP(net string,raddr *UDPAddr) (c *UDPConn,err error) func DialIP(netProto string,raddr *IPAddr) (*IPConn,error) func DialUnix(net string,raddr *UnixAddr) (c *UnixConn,err error) http库,net/http包 func (c *Client) Get(url string) (r *Response,err error) func (c *Client) Post(url string,bodyType string,body io.Reader) (r *Response,err error) func (c *Client) PostForm(url string,data url.Values) (r *Response,err error) func (c *Client) Head(url string) (r *Response,err error) func (c *Client) Do(req *Request) (resp *Response,err error) http服务端编程 http.Handle("/foo",fooHandler) http.HandleFunc("/bar",func(w http.ResponseWriter,r *http.Request) { fmt.Fprintf(w,"Hello,%q",html.EscapeString(r.URL.Path)) } ) log.Fatal(http.ListenAndServe(":8080",nil)) 或者是 s := &http.Server{ Addr:":8080",Handler:myHandler,ReadTimeout:10 * time.Second,MaxHeaderBytes: 1 << 20,} log.Fatal(s.ListenAndServe()) https有点不同 log.Fatal(http.ListenAndServeTLS(":10443","cert.pem","key.pem",nil)) RPC net/rpc直接支持 服务端 arith := new(Arith) rpc.Register(arith) rpc.HandleHTTP() l,e := net.Listen("tcp",":1234") if e != nil { log.Fatal("listen error:",e) } go http.Serve(l,nil) 客户端 client,err := rpc.DialHTTP("tcp",serverAddress + ":1234") if err != nil { log.Fatal("dialing:",err) } args := &server.Args{7,8} var reply int err = client.Call("Arith.Multiply",args,&reply) if err != nil { log.Fatal("arith error:",err) } fmt.Printf("Arith: %d*%d=%d",args.A,args.B,reply) Gob Gob 是 Go 的一个序列化数据结构的编码解码工具 json处理 压缩一个数据,放到另外一个数据里面 func Marshal(v interface{}) ([]byte,error) func Unmarshal(data []byte,v interface{}) error �� JSON中的布��值将会��换为Go中的bool类型; �� 数值会被��换为Go中的float64类型; 字符����换后还是string类型; JSON数组会��换为[]interface{}类型; JSON对象会��换为map[string]interface{}类型; �� null值会��换为nil。 md5和sha1 package main import( "fmt" "crypto/sha1" "crypto/md5" ) Md5Inst:=md5.New() Md5Inst.Write([]byte(TestString)) Result:=Md5Inst.Sum([]byte("")) fmt.Printf("%x\n\n",Result) Sha1Inst:=sha1.New() Sha1Inst.Write([]byte(TestString)) Result=Sha1Inst.Sum([]byte("")) fmt.Printf("%x\n\n",Result)