Archive for golang

golang博客里有一个例子。下面两段程序的结果是相同的。

package main
import (
    "fmt"
    "reflect"
)

type A struct {
    Name string
}
func (p A) test(){}
func (p A) Test1(){
    fmt.Println("df")
}

type AInterface interface {
    Test1()
    test()
}

func main() {
    var a A
    a.Name = "hha"
    var value reflect.Value = reflect.ValueOf(a)
    p := value.Interface().(AInterface)
    p.Test1()
}
package main
import (
    "fmt"
    "reflect"
)

type A struct {
    Name string
}
func (p A) test(){}
func (p A) Test1(){
    fmt.Println("df")
}

func main() {
    var a A
    a.Name = "hha"
    var value reflect.Value = reflect.ValueOf(a)
    vf := value.MethodByName("Test1")
    in := make([]reflect.Value, 0)
    vf.Call(in)
}
遗憾的是第一种方法事先定义好了interface,如果你加入的新方法应该是不能成功的。

Continue

没写完,先这样吧,应该要停写一段时间,先放出来。我又开始研究别的了0 0,golang又要放下了,不过我觉得,那个消耗的时间不会长,回头再接着写^_^. 我的第一个web框架,用go写。虽然没啥价值^_^ 靠,忘记写地址了0 0:github

Continue

从上上个周开始,就开始进行了。为啥起了这么蛋疼的一个名字,0 0当时建目录的时候不知道起啥名字,然后就起了一个the first web framework(tfwf),然后在bitbucket上开了一个私有项目,就开始写了。 先说说目的:一直都在用别人写的东西,包括接触的n多东西,一直想自己也实现一个。终于下决心用go准备写一个web框架,因为对这个还算是熟悉的了。选择go的原因是,写了可能会有别人用,但是我的出发点是学习并玩玩。不懂设计,不懂高深的理论,能有点样子就好了。 其实go自带的包写web应用已经非常方便了,现在主要实现的功能有: url的正则匹配,并且可以定义`^/aa/(?P<second>\d+)/`这样的样式。 网站的基本设置,像域名,名字,templedir等 控制器模仿tornado的样子写的,感觉蹩脚的很0 0. 启动时模板的导入。 就这些了。   说说从一开始写的过程,现在只记得七七八八了,时间有点长了。 准备写web框架的时候,我先去大略看了一遍谢大的 goweb编程那本书,第3.4节是对web框架开头路由的编写起了关键性的作用。 然后我就直接从http包 函数找到源码,把那里的代码意思看懂,就开始写了支持正则匹配的路由,然后对应的handler是函数。之后我就想写成python里边面向对象可以重载的方法,就研究了一下go的面向对象的编程,写成了接口的模式,什么走路像鸭子就是鸭子0 0。。写这里的时候我遇到一点问题:从接口取到对应struct的元素和方法,我有去看来reflect包的内容。 这期间我找了,web.go和beego的代码,大体看来一遍,发现beego的控制器写的跟我想的基本一样,直接看代码,借鉴了很多代码- -算不算抄袭。像url里匹配的变量,我发现go不支持继承的方法参数啥的必须一样。我就在context里边加了一个属性args,感觉真二。 今天开会的时候又加上了temple的启动时集中导入功能,功能的来源beego。再可耻一下0 0.settings和templates都是全局的,这些都不需要修改,第一次启动添加,应该不会有问题。 现在还有好多细节没处理好。还有很多必要的功能没有,像:错误提示,我自己写的时候测试,都发现非常困难,只是自己比较了解,能快速定位刚才修改的代码。我现在很多代码的错误验证都还没有0 0.、 对 一个主要的功能准备,明天实现:异步的问题,现在还没头绪,明天仔细研究一下,并测试一下在普通http请求这两个差别真的很大吗。我现在只是直接调用方法。觉得挺直观明白的0 0. 代码明天实现异步在上传到github上,现在也没格式化。我觉得我应该用我自己写的框架写点能用的东西,才知道还有哪里需要修改的更好0 0.

Continue

又传到github上了:https://github.com/0x55aa/golang-udp-chat 用golang写的第二个程序,这么久再没学golang,找到《go语言编程》的电子版,现在打算重新再学一遍。翻出这个程序,分享。 没有什么特殊功能,自己边做边添加,主要使用了net包中关于upd的几个函数。还没有实现预期的功能,只是个雏形,传上去好记得还写过这么个小程序,哪天心血来潮再改改。 程序是根据这个程序修改的《Network programming with Go》,下面我想看完电子书开始用go写web程序,路漫漫其修远兮 吾将上下而求索。 截了client的图: golang聊天程序

Continue

总结的很好,《golang web 编程》作者维护的golang博客:http://beego.me/ 。。。。。。。。。。。。。。。。 我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:

import(
    "fmt"
)
然后我们代码里面可以通过如下的方式调用
fmt.Println("hello world")
上面这个fmt是Go语言的标准库,他其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块: 1.相对路径 import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import 2.绝对路径 import “shorturl/model” //加载gopath/src/shorturl/model模块 上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事 1.点操作 我们有时候会看到如下的方式导入包
import(
    . "fmt"
)
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world") 2.别名操作 别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
import(
    f "fmt"
)
别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world") 3._操作 这个操作经常是让很多人费解的一个操作符,请看下面这个import
import (
    "database/sql"
    _ "github.com/ziutek/mymysql/godrv"
)
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数,要理解这个问题,需要看下面这个图,理解包是怎么按照顺序加载的: 程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它 只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先 将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开 始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过 程: [caption id="" align="alignnone" width="569" caption="golang import"]golang import[/caption]   通过上面的介绍我们了解了import的时候其实是执行了该包里面的init函数,初始化了里面的变量,_操作只是说该包引入了,我只初始化里面的 init函数和一些变量,但是往往这些init函数里面是注册自己包里面的引擎,让外部可以方便的使用,就很多实现database/sql的引起,在 init函数里面都是调用了sql.Register(name string, driver driver.Driver)注册自己,然后外部就可以使用了。 这样我们就介绍完了全部import的情况,希望对你理解Go的import有一定的帮助。

Continue

很不错的教程,讲的很详细。《Network programming with Go》http://jan.newmarch.name/golang/,照着教程写了一个小程序。  

//server.go
package main

import(
    "fmt"
    "net"
    "os"
    "time"
)

func handleClient(conn *net.UDPConn){
    var buf [512]byte

    n, addr, err := conn.ReadFromUDP(buf[0:])
    if err != nil{
        return
    }
    fmt.Println(string(buf[0:]))
    daytime := time.Now().String()
    fmt.Println(n,addr)
    conn.WriteToUDP([]byte(daytime),addr)

}

func checkError(err error){
    if err != nil{
        fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
        os.Exit(1)
    }
}

func main(){
    service := ":1200"
    udpAddr, err := net.ResolveUDPAddr("up4",service)
    checkError(err)

    conn,err := net.ListenUDP("udp",udpAddr)
    checkError(err)

    for{
        handleClient(conn)
    }
}
//client.go
package main

import(
    "fmt"
    "net"
    "os"
)

func checkError(err error){
    if err != nil{
        fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
        os.Exit(1)
    }
}

func main(){
    if len(os.Args) != 2{
        fmt.Fprintf(os.Stderr, "Usage:%s host:port", os.Args[0])
        os.Exit(1)
    }
    service := os.Args[1]
    udpAddr, err := net.ResolveUDPAddr("up4",service)
    checkError(err)

    conn,err := net.DialUDP("udp",nil,udpAddr)
    checkError(err)

    _,err = conn.Write([]byte("hello"))
    checkError(err)

    var buf [512]byte
    n,err := conn.Read(buf[0:])
    checkError(err)
    fmt.Println(string(buf[0:n]))
    os.Exit(0)
}
,以后再扩展功能,先睡觉。

Continue

func Join(a []string, sep string) string
,连接字符串,以sep作为分隔符 例子:
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
输出:
foo, bar, baz
,源代码分析 1.判断a的长度,0时,返回空字符串,长度为1时,返回a[0]. 2.否则,在变量n中保存将要生产字符串的长度。计算方法len(a)-1个sep的长度 + 数组a中字符串长度和。 3.创建一个长度为n的byte数组。将数据复制到数组中,copy()将返回复制元素的个数,用bp来记录复制的位置。最后转成string返回。  
func Split(s, sep string) []string
,分割字符串,sep为空字串时,与上一篇讲到的一样,相当于每个字符之间的间隔。s中没有sep,返回值里只有一个元素s。 例子:
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
输出:
["a" "b" "c"]
["" "man " "plan " "canal panama"]
[" " "x" "y" "z" " "]
[""]
,源代码return genSplit(s, sep, 0, -1) 看看func genSplit(s, sep string, sepSave, n int) []string。
func genSplit(s, sep string, sepSave, n int) []string
,有几个函数都用到这个函数,通用分割字符串的函数。 n 是返回分割的个数,-1时,返回所有。
sepSave是被分割的位置在sep所在位置的偏移,被用于SplitAfter和SplitAfterN这些函数,使用len(sep)作为sepsave可以返回sep最后一个字符前面的字串作为一个分割。
1.n为0时,返回nil。 2.sep为空字串,return explode(s, n), 3.当n<0时,应该返回所有,n为Count(s, sep) + 1,表示长度。 4.定义几个变量,c是用于比较是否和sep相等,都是先比较第一个字符。相等再看字符串长度为一,则找到;不为一,比较整个sep。a是被返回的数组,na是在数组中存储的位置。 东西零碎 utf8包的内容,写下来的目的是使自己是真正的明白,而不是一看知道是这么回事,但是不想为什么要这样写。现在感觉自己用来学习的时间很少,珍惜一下。

Continue

打算把golang的常用包都看一遍,分析一下包的代码,觉得从中会学到golang的编程方法^_^。

func Index(s, sep string) int
返回s中第一次出现sep的位置,如果没有返回-1. 例子: 代码:
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
输出:
4
-1
看源码中index的实现方法, 1.取得sep的长度。如果长度为零,返回0。否则取sep第一个字符。 2.如果长度为1,for循环比较s[i]和sep[0],相等,返回i。都不相等,返回-1。 3.如果长度大于1,for循环比较(先比较s[i]和sep[0],不相等就i++,再比较s[i:i+n] == sep。)成立返回i,否则返回-1。比较的上限是i+n。 相关函数:  
func Contains(s, substr string) bool
如果substr在s中,返回true。 代码:
fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
输出:
true
false
true
true
源代码中用index函数实现,return Index(s, substr) >= 0。  
func LastIndex(s, sep string) int
,返回在s中最后一次出现sep的位置,不存在返回-1。 例子就过了,源代码分析,与index()相似,但是判断的时候是从s的后边向前判断。  
func Count(s, sep string) int
,统计sep在s中出现的次数,非重叠的,比如s=“eeee”,sep="ee",结果返回2. 例子:
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
结果:
3
5
,我对第二个例子不理解,在群里问了一番,开始回答的我没明白。后来第二个给我回答我才明白:这里“”字符串指的就是字符串中每个字符之间的空字符串T_T,函数返回了s中有几个这样的空字符串。后面的英文注释直接看不懂T_T,悲催。现在看应该是每个字符的前面加后面。。。 源代码中“”时,返回utf8.RuneCountInString(s) + 1,这里不用len,我测试了一下对于一个中文len返回的长度是3,这里不符,所以用utf8.RuneCountInString。utf8.RuneCountInString是使用range来遍历的字符串,与len计数不同,一个中文长度为1。 但是在接下来遍历s和sep都是用的len(),也就是说比如 fmt.Println(strings.Count("我的我的", "\x88"))你会得到结果2,这是因为 ”我的“会被\xe6\x88\x91\xe7\x9a\x84遍历,你将从中找到\x88。 fmt.Println(strings.Count("five", "")) 然后还是分两种情况,len(sep)=1和大于1两种情况。  

Continue

在看《build web application with golang》地址:https://github.com/astaxie/build-web-application-with-golang 看到Panic和Recover这一节,对Panic和Recover有了了解。 Go没有例如像Java那样的异常机制:不能抛出一个异常。作为代替,它使用了panic和recover机制。一定要记得,这应当作为最后的手段被使用,你的代码中应当没有,或者很少的令人恐慌的东西。这是个强大的工具,明智的使用它。那么,应该如何使用它。 Panic

是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执 行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函 数返回,此时程序退出。恐慌可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。
Recover
是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。Recover仅在延迟函数中有效。在正常的执行过程中,调用 recover会返回nil,并且没有其他任何效果。如果当前的goroutine 陷入恐慌,调用recover可以捕获到panic的输入值,并且恢复正常的执行。
最容易理解就是给个例子,文章里有例子:
package main

import(
    "fmt"
    //"os"
)

var user = ""
func inita() {
    defer func(){
        fmt.Print("defer##\n")
    }()
    if user == "" {
        fmt.Print("@@@before panic\n")
        panic("no value for user\n")
        fmt.Print("!!after panic\n")
    }
}

func throwsPanic (f func()) (b bool){
    defer func(){
        if x:= recover(); x != nil{
            fmt.Print(x)
            b = true
        }
    }()
    f()
    fmt.Print("after the func run")
    return
}

func main(){
    throwsPanic(inita)
}
执行结果: D:\go>go run b.go @@@before panic defer## no value for user 如上面所说的: panic在user=""时,打断了函数的执行,fmt.Print("!!after panic\n")没有执行。 但函数中的延迟函数会正常执行,打印了 ”defer##“。然后返回到调用该函数的地方,继续上面的过程。 直到执行完所有函数的defer,退出程序。 Recover可以捕获到panic的值,上面的打印“no value for user”。并且恢复正常的执行。

Continue

学习golang来自己写的代码,先贴出来。

package main
import (
    "fmt"
    "os"
    "errors"
    "strings"
)
//no tag 
var ErrFormat = errors.New("No TAG!")
type Mp3 struct{
    //the file attr
    Name string
    Size int64
    //the mp3 attr
    Title string
    Artist string
    Album string
    Year string
    Comment string
    Genre uint8
}
func (m *Mp3) PrintInfo(){
    fmt.Printf("%v:\n",m.Name)
    fmt.Printf("size:%v\ntitle:%v\nartist:%v\nalbum:%v\nyear:%v\ncomment:%v\n",m.Size,m.Title,m.Artist,m.Album,m.Year,m.Comment)
    fmt.Printf("genre:%v",m.Genre)
}
func (m *Mp3) GetInfo(filename string) (err error){
    id3 := make([]byte,128)
    //for read access.
    f,err := os.Open(filename)
    if err != nil{
        return
    }
    defer f.Close()
    //return FileInfo
    fileInfo,err := f.Stat()
    if err != nil{
        return
    }
    //the file size,int64
    m.Size = fileInfo.Size()
    //the file name,string. eq filename
    m.Name = fileInfo.Name()
    //if
    _,err = f.ReadAt(id3,m.Size-128)
    if err != nil{
        return
    }
    tag := string(id3[:3])
    if tag != "TAG"{
        //err  "No ID3~"
        return ErrFormat
    }
    m.Title = strings.Trim(string(id3[3:33]),"\x00")
    m.Album = strings.Trim(string(id3[33:63]),"\x00")
    m.Artist = strings.Trim(string(id3[63:93]),"\x00")
    m.Year = string(id3[93:97])
    m.Comment = strings.Trim(string(id3[97:127]),"\x00")
    m.Genre = uint8(id3[127])
    return nil
}
func main(){
    m := new(Mp3)

    f,err := os.Open(".")
    if err != nil{
        fmt.Print(err)
    }
    defer f.Close()
    names,err := f.Readdirnames(-1)
    if err != nil{
        fmt.Print(err)
    }
    for _,name := range names{
        a := strings.Split(name,".")
        fileExt := strings.ToLower(a[len(a)-1])
        //fmt.Print(i,fileExt)
        if fileExt == "mp3"{
            err = m.GetInfo(name)
            if err != nil{
                fmt.Print(name,err)
            }
            m.PrintInfo()
        }
    }
    //fmt.Printf("%v",names)
}
有关id3的介绍,程序只读取id3 vi版本的信息,位于mp3文件末尾的128个字节。 数据结构定义如下: char Header[3]; /*标签头必须是"TAG"否则认为没有标签*/ char Title[30]; /*标题*/ char Artist[30]; /*作者*/ char Album[30]; /*专集*/ char Year[4]; /*出品年代*/ char Comment[30]; /*备注*/ char Genre; /*类型*/ ID3V1的各项信息都是顺序存放,没有任何标识将其分开,比如标题信息不足30个字节,则使用'\0'补足,否则将造成信息错误。

Continue