最近又打算玩玩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

init()函数main前调用

函数

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() //获取存储在第一个字段里面的值

访问mysql数据库

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", 0, 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, off int64) (n int, 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, 0, 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", 10, 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","127.0.0.1:8888")
    conn,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, laddr, raddr *UDPAddr) (c *UDPConn, err error) 
    func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error)
func DialUnix(net string, laddr, 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,
                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