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