语言学习作者资深码农一枚没什么名气也学历教程中肯定会有_第1页
语言学习作者资深码农一枚没什么名气也学历教程中肯定会有_第2页
语言学习作者资深码农一枚没什么名气也学历教程中肯定会有_第3页
语言学习作者资深码农一枚没什么名气也学历教程中肯定会有_第4页
语言学习作者资深码农一枚没什么名气也学历教程中肯定会有_第5页
已阅读5页,还剩179页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

前 “反射”之后紧接着就是如何实现一个ORM,实用性强,注重实际应用。再比如网络编程一节,会讲设计一个TCP和UDP程序的不同,TCP程序需自定义通信格式,UDP需要双方约定UDP包的大小。本 的第最后一章向大家介绍一个GoMvc框架。:第1前 第一章GO语言的安 安装 安装go语言开发工 安装 第二章GO语言基 基本类 定义变 数 切片 常 控制 if 2.52 for循 第三章函 函数定 多值返 变参函 函数类 错误处 第四章面向对象编 继 第五章多线 多线 进程同 第六章日期与定时 日期的获取与计 定时 路 文件读 遍 下的文 第2gob序列 第8章JSON与XML解 XML序列化与解 JSON序列化与反序列 第9章MySQL数据库操 安装MySQL驱 MySQL数据库操 事 标准驱动的不足与改 第10章反 反射基 反射调用函 反射取Struct的Tag信 第11章实现一个自己的 实现自己的 Insert函数的实现及所有源 第12章TCP与UDP网络编 第十三章WEB编 第一个WEB程 文件上 模板展 第十四章GoMvcWeb框 MVC简 GoMvc简 结 配置文 路 第3第一章GO语言的安装goGo语言是由 语言语法灵活、简洁、清晰、高效。它对的并发特性可以方便地用于多核处理器和网络开内存自动回收功能,并且还支持运行时反射。Go是一个高效、静态类型,但是又具有Golang官网 ,在国内可能打不开,可以使用goagent 以windows为例,首先从go语言官网http 新的go安装包。将其解压到本地硬盘。 的安装只要设置相应的环境变量就可以了,相关环境变量如下 第4GOBIN表示编译器和器的安装位置,默认是$GOROOT/bin,如果你使用的是Go1.0.3及以后的版本,一般情况下你可以将它的值设置为空,Go将会使用前面提到的默GOARCH386,amd64arm我的机器是windows64位,所以设置为amd64。如果你的机器是32位的设置为386我用的是windows,所以设置为windows。GOMAXPROCS:用于设置应用程序可使用的处理器个数与核数第5GOMAXPROCS环境变量,程序只使用一个线程。为了利用全部CPU内核,则必须制定它的值。我的CPU是i7,四核,所以该变量本人设置为4GOPATHGo项目源代码和二进制文件的。GOPATH允许多个,当有多个GOPATHWindows是分号,Linux系统是冒号,当有多个GOPATHgoget的内容放在第一个下。以上 src存放源代码(比如:.go.ch.s等pkg编译后生成的文件(bin编译后生成的可执行文件(为了方便,可以把此 加入到$PATH变量中) 这里我把k:\go做我工 。如下图所示warning:warning:GOPATHsettoGOROOT(E:\go)hasnoeffectOT.Formoredetailssee:gohelpgopath填%GOBIN%,或直接写路径如e:\go\bin。开始->运行->输入cmd,打开命令行窗口,输入go回车,出现下图信息,说明安装成功第6至于其它系统的安装,大同小异,如在安装过程中遇到问题可 一下安装go语言开发工 liJIDEALiteIDE是一款专为Go语言开发而设计的跨平台轻量级集成开发环境(IDE),基于Qt开发,支Windows、LinuxMacOSX平台。LiteIDE的第一个版本发20111月初,是最早的面向Go语言的IDE之一。 版本。解压到本地硬盘第7我是放在了E:\Program 下。右击liteide.exe,在弹出的右键菜单中选择发送到->桌面快捷方式。在桌面上他建快捷方式旁边的黑色按扭,编lite的环境配置。##nativecompilerwindowsamd64第8别忘了保存编辑后的文件文件->新建,打开新 框,名称输入test_go。点击OK创建一个命令行项目在菜单中选择Build->BuildAndRun,编译并运行程序第9 liJ liJIDEA被认为是当前Java开发效率最快的IDE工具。它整合了开发过程中实用简单而又功能强大。与其他的一些繁冗而复杂的IDE工具有鲜明的对比。In liJIDEA下有golang插件,技持go的开发。打开 liJIDEA,点选Configure,如下图所示再选Plugins,如下图所示10在 框中,点Browserepositores...,如下图所11.在新窗口中找的插件,右击->选择”DownloadandInstall”12关闭当前窗口,返回 框,找到golang插件,点击选择,再点右下角的按钮重启 liJIDEA,如下图所示13重启后,点CreateNewProjiect,打开新建项目的 ,要左则选择GoModule,在右边14gogogoProjectSDK右边的”New...”golang的安装位E:\go下Gocode是的Go语言的一个代码自动补全的工具,对于windows程序员应该很熟悉VS的代码提示功能,很强大。在不安装gocode的情况下,LiteIDE是没有代码提示的,所以最好能装gocode。经测InliJIDEA不需要安gocode就可以代码InliJIDEA的代码提示功能要比LiteIDE强大。Gocode 上的一个开源项目,地址是 /nsf/gocode。开->运行->输入cmd打开命令行窗口。输入如下命令来安装gocode如果提示下面的信息,说明没有安装git。 版本15并安装go:missinggo:missingGitcommand./nsf/gocode:exec:"git":executablefilenotfoundinRunGitfromtheWindowsCommandPrompt,这样就不需要手动在Path里面设置git的路径,可以在命令行运行git命令。安装成功后,关闭原来打开令窗口,再重新打开一个命令窗口,重新运行上面的gocode令。命令执行成功后gocode将安装GOPATH的位置,我设置的是K:\go,gocode将会安装在这个。如下图所示:1617第二章GO语言基Go程import()func{o第一行,package定义了程main。第二import引入了fmt包。funcmain定了main函数,func是函main函数调fmt包的Println函数,输出了oWorld!”在所有初始化完成后,程序从main包中的main函数开始执行Go中的所有字符串,都UTF-8编码。所有的Go语言源文件也都是采UTF-8编码。在Go语言中,语句末尾的分号可以省略不写。基本类 对整数进行了更明确的规划,清晰明了。Go里的基本类型如下表类长度(字节说11uint84 Unicode432bit64bit11-128 127; 2- 32767; 4-21亿 21亿 428复数类型,即32位实数+32复数类型,即64位实数+643264数组,值类型,如:[2]结构体,值类类型如 类类18接口类函数类定义变Go语言里面定义变量有多种方使用var关键字是Go最基本的定义变量方式,最常见的语法如下varn /*定义变量variint=3 strvari1,i2,i3int=1,2,3/*定义多个变量并赋值var ”;/*Go会自动检测变量的类型packagemain funcmain(){varbvarnpackagemain funcmain(){varbvarn /*定义变量variint=3/*定义变量i并赋值3*/var( strstring)vari1,i2,i3int123/*定义多个变量并赋值varstrName /*Go会自动检测变量的类型strSex"男/*:=定义变量,并给变量赋值,可以省略var关键fmt.Println("n=",fmt.Println("b",fmt.Println("i=",i)fmt.Println("aa=",aa)fmt.Println("strName=",strName)fmt.Println("strSex=",strSex)}编译并运行程序,你会发现b=false,n=0;对于未赋值的变量,Go会自动初始化,数值 类型初始值为false,字符串初始值为空。19数array是固定长度的数组,这个和C语言中的数组是一样的,使用前必须确定数组长度。但是和C中的数组相比,又是有一些不同的:o中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份如果Go中的数组作为函数的参数,那么实际传递的参数是一份数组的拷贝,而不是数组的指针。这个和C要区分开。因此,在Go中如果将数组作为函数的参数传递的话,那效array的长度也是Type的一部分,这样就说明[10]int和[20]intarray的结构用图示表示是这样len表示数组的长度,后面的int vararr_1[2]int/*一个2个元素的数组arr_2,并同时赋初值,{}里为空,说明没有赋初值,arr_2:= arr3:=[2]int{1,2}arr3_1:=[2]int{0:1,1:2}arr3_2:=/*不指定数组长度,自动计算长度,[...],一个2个(自动计算而来)元素的数组,名arr4,并同时赋初值,结果为[1,2]*/arr4:= 一个4个(动计算而来)元素的名字为shuzu5时赋初值,结果为完整代码如下20 一个2个元素的数组,名字为arr_1,因为是int型数组,所以初值为0,即[0,0]vararr_1[2]intimport()funcmain()为为}*不指定数组长度,自动计算长度,[...], 为arr4,并同时赋初值,结果为[1,2]*/arr4:=[...]int{1, 一个4个(自动计算而来)元素的数组,名字为shuzu5,并同时赋初值arr3:=[2]int{1,arr3_1:=[2]int{0:1,1:2}arr3_2:=[2]int{1:2,0: 特定的空间,或者其它数组的空间。在Go语言中Slices比数组使用的更为普遍,因为它更有弹性, []T是一个T类型的片,切片不需要指定长度,指定长度就成了数组。切片可以被重新分片。创建一个指向同一数组的指针。s[lo:hi]lohi-1s[lo:lo]是空的。21的数组p的数组p第一个和第二个元素,所以数组p的值被改//切片packagemainimport"fmt"funcmain(){s1:=p[1:3] fmt.Println(reflect.TypeOf(s1))//s1是切片类型[]intChangeArrayValue(p)//ChangeArrayValue函数将第一个值改为100 //数组p的值并没有改变,因为数组是值类型 }}{arr[0]=}{slice[0]=}。key必须是支持比较运算符(==、!=)的类型。如number、string、pointer、array、Map用make来分配内存空间,mak(map[TK]TV),TK是key的类型,TVimport()funcmain()mp:=make(map[string]string)//key是字符串类型,值也是字符串类mp["a"]=mp["b"]=mp["pi"]= mp["sh"]="22ififok}elsefmt.Println("keysh''不存在}ifok{}elsefmt.Println("key'bj'不存在}}import()funcmain()arr:=[3]int{1,2,varmp=map[int]string{1:"a",2:"b",3:"c"}fork,v:=rangemp{}for_,v:=range{}}常常量必须是编译期能确定的,常量的定义使用const,常量的类型可以是char以被编译器求值。例如,1<<3是常量表达式,math.Sin(math.Pi/4)不是,因为math.Sin的函数调用发生在运行态。23constconstconstUserName,SexconstUserName,Sex=funcmain() }值。按行递增,可以省略后续行的iota关键字。constconst)/*Sunday=0Monday=1Tuesday=可以在同一行使用多个iota,它们各自增长funcfunc{constU,V=iota,iotaW,XY,)/*U=0V=0W=1X=1Y=2Z=fmt.Println("U=",U,"V=",V,"W=",W,"X=",X,"Y=",Y,"Z=",}如果某行不想递增,可单独提供初始值。不过想要恢复递增,必须再次使 func{conststr 24 //没有赋值,跟上一行一样,要想恢复自增,需再次赋值iota =iota)/*A1=0A2=1 o oA3=4A4=}}ifa:=a:=ifa==2}在if和条件之间可以包括一个初始化表达式,上面的代码码可以写成ififa:=2;a==2}在if条件里初始化的变量,作用域是这个if,语句块,如上面代码中的变量a,只能在ifa:=2;a<2}elsefmt.Println("a="ifa:=2;a<2}elsefmt.Println("a=",}下面的代码是错误的,因为if和条件之间只能有一个初始化表达25ififa:=2;b:=100;a==2}2.52Goswitch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果switch没有表达式,它会匹配true。Go里面switch默认相当于每个case最后带有break,匹配成功后不会自 他case,而是跳出整个switch,但是可以使用fallthrough强制执行后面的case代码。funcfunc{i:=switch{casefmt.Println("iisequalto1")case2:fmt.Println("iisequalto2")case3456//case可以有多个值fmt.Println("iisequalto3,4,5or}}不指定switch条件表达式,或直接为true时,可用于替代if...else...if...elsefuncfunc{result:=ifresult<0fmt.Println("小于零}elseifresult>}elsefmt.Println("等于零上面的代码可改写为switch看起来更加清晰明了26 {result {caseresult<caseresult>0:fmt.Println("等于零forGo只有一个关键字用于引入循环。但它提供了除do-while外C语言当中所有可用的循Go的for循环有如下三种形式forforinit;condition;post←Cforfor ←死循初始化表达式计算。在for初始for中,i,jfor句中有效。str:=str:=funcmain()forfori,j:=0,len(str);i<j;{}}func{i:=forififunc{i:=forifi>10}}}27嵌套循环时,可以在break后面指 ,用来指定要终止哪儿个循环funcfunc{fori:=0;i<5;i++{fork:=0;k<5;k++{ifi>0break}}}} 用于终止本次循环体的执行继续执行下一个循环。下面是打印非空格符strstr:= ofori,j:=0,len(str);i<j;i++ifstring(str[i])==""}}与break相同,对于嵌套循环,可以 ,来指定要继续哪一层循环funcfunc{fori:=0;i<5;i++fork:=0;k<100;{ifk>0continue}}}}28第三章函数函数定Go是面向过程的编程语言,函数是Go程序的基本部件funcfuncAdd(a,bint){returna+}func是定义函数的关键字,Add是函数名;int是返回值。可以随意安排函数定义的顺序,Go编译时会扫描所有的文件。packagepackageimport)func{}func{}func{}多值返GoGo语言中是经常被用到的,比如,一个函数同时返回结果和异常。例如打开文件的函数funcOpen(namestring)(file*File,errerror)。package下面我们看一个例子,divide函数是计算a/b的结果,并返回商和余package29importimport)funcdivide(a,bint)(int,{quotient:=a/bremainder:=a%breturnquotient,}funcmain()q,r:=divide(5,3)fmt.Println(q,",",r)}return上面的divide函数,可以使用命名的返回值,如下funcfuncdivide(a,bint)(quotient,remainder{quotient=a/bremainder=a%b}变参函Goslice,且必须是最后一个参数。将sliceimport(){s:=for_,number:=range{s+=}return}30funcfuncmain()total:=sum(1,2,3,4)sliceint{123456789//等价于sum(1,2,3,4,5,6,7,8,9)}defer是Go语言所特有的,defer的作用是延迟执行,在函数返回前,按照后进先出的原则defer的函数。这样可以保证,函数在返回前被调用,通常且来进import(){f,err:=os.Open(strFileName)iferr!=nil{return"",}deferf.Close//在函数返回前关闭文件buf:=make([]byte,1024)varstrContentstring=""for{ifn==0{}strContent+=}returnstrContent,}funcmain()str,err:=31ififerr!=nil}}函数类函数也是一种类型,,拥有相同参数,相同返回值的函数,是同一种类型。用type来定义函数类型。下面的Display函数输5的数值。import()typeMyFuncTypefunc(int)boolfuncIsBigThan5(nint)bool{returnn>}{for_,v:=rangearr{iff(v){}}}funcmain()arr:=[]int{1,2,3,4,5,6,7,8,}在上面的例子中,typeMyFuncTypefunc(int)bool定义了一个函数类型,将其命名为MyFuncType,接受一intbool类型的结果。IsBigThan5MyFuncType类型的函数。函数类型,跟C里的函数指针有点像。错误处Go语言中没try...catch...finally这种结构化异常处理,而是panicthrow抛出异常。使recover函数来捕获异常。Recoverdefer函数中使用才能捕获异常州,此时函数32import()func{defer{iferr:=recover();err!={}divide(5,0) fmt.Println("endoftest") }funcdivide(a,bint){returna/}func{33Go是面向过程的语言,GoGo支持面向对像的编程,Gostruct就像其它语中的类;Go里没有继承,但可以用组合来实现。结构体是一种自定义类型,是不同数据的集合体struct是值类型。通常用来定义一个抽像的数据对像,比如学生,可以有、、班级等数据构成,struct值类型。结构typetypeStudentstruct成员名字1类型1成员名字2类型2成员名字3类型}可以用New来创建结构体,然后对各字段进行赋{Namestring classstring}funcmain()s1.Name= s1.Age=12s1.class"21班"}除了上面这种方式,还可以使用以两种式定义,初始化结构按照顺序提供初始化 ”,12,”2班通过field:value 班Go语言没有class,不支持面向对象。但支持面向struct结构体的成员函数,使用方法34面向对象编程方式相似。定义格式如func)函数名(参数列表返回值类型列表s:=Student{Name:{s:=Student{Name:{Namestring classstring}func(thisStudent)getName(){return}//结构体可以传指针类{return}funcmain()}继Goclass,但可以把structclass来看Go不能class那样继承可以通 字段来实现继承packagepackagemain {Namestring classstring}func(this*StudentDisplay}//定义一个大学生类,继承StudenttypeCollegeStudentstruct{}35s1s1:=CollegeStudent{Student:Student{Name:Profession:"物理Profession:"物理) }CollegeStudentStudent的所有字段和方法。CollegeStudent也可以重写继承的方funcfunc(this*CollegeStudent)}上 ,大写开头的相当于public,小写开头的是private,包外是不可

"Age23class:"2004(2)班funcmain()如果包a中的一个结构,去继承b包中结构体,只有大写"Age23class:"2004(2)班funcmain()typeStudentstruct{ classstring/*小写开头的包外不可见,包外的结构体也无法继承该字段}下面我们在main包中继承import(){}funcmain()s:=MyStudent{}s.Student.class="aaaa"}编译时将报错,s.Student.classundefined(cannotrefertounexportedfieldormethodclass)。 36包名必须跟所 名一至import()typeFruitstruct}func(this*Fruit){}func(this*Fruit)GetName()return}typeApple{}func(this*Apple)GetName()return}func{fruit:=apple:=Apple{}}上面代码运行结果为37在上面的例子中,Apple继承了Friut,并且重载了GetName函数,我们期望的结果重写Display函数,这样做是绝对没有问题。但通常Display里是一些通用的业务逻辑,我不想在每个类中都去重写这个函数,这样不利于代码的。要想实packagepackageimport)typeFruitNamefunc()typeFruit{GetFruitName}func(this*Fruit){}func(this*Fruit)GetName()return}funcNewFriut(){f:=new(Fruit)f.GetFruitName=f.GetNamereturnf}typeApple{}func(this*Apple)GetName()return}funcNewApple(){a:=new(Apple)a.GetFruitName=a.GetNamereturna}funcmain()fruit:=38appleapple:=NewApple()}上面程序的运行结果上面例子中先用typeFruitNamefuncstring定义了一个FruitName函数类型,然后在Friut结构体中定义了一个FruitName函数类型的成员GetFruitName。在NewAppleNewFriut中对GetFruitName进行了赋值,在DisplayName函数中调用了GetFruitName。接口是一系列操作的集合,是一种约定。我们可以把它看作与其它对象通讯的协议。任接口类型只要拥有某接口的全部方法,就表示它实现了该接口,o中无需显式在该类上添加接 {Namestring classstring}{GetName()stringGetAge()int}{return}func(this*Student)GetAge(){return}funcmain()vars1IStudent "23"2004(2)班}Interface{}没有定义任何方法,称为空接口。任何类型默认都实现 interface{},相39于C语言中的void*指针40第五章多线程线程是CPU调度的最小单位,只有不同的线程才能同时在多核CPU上同时运行。但线程太占资源,线程调度Go中的goroutine是一个轻量级的线程,执行时只需要4-5K的内存,比线程更易用,更高效,更轻便,调度开销比线程小,可同时运行上千万个并发。Go语言中来启一个goroutine非常简单,Go函数名(),就开启了个线程。默认情况下,调度器仅使用单线程,要想发挥多核处理器的并行处理能力,必须调用runtime.GOMAXPROCS(n)来设置可并发的线程数,也可以通过设置环境变量GOMAXPROCS达到相同的目的(在第一章,安装Go中有介绍。Runtime包中提供了几个与goroutine有关的函数。Gosched()让当前正在执行的goroutine放弃CPU执行权限。调度器安排其它正在等待的线程运行。如下面的程序,开启两个 o,一个输出world。go go }}fori:=0;i<10;{)}}funcmain()ofori:=0;i<10;func o()import()gogotime.Sleep(5*}上面程序的运结果是输出几行

oWorld!,首先启动了一个

o线程,接着起动SayWorld线程,在

o线程中输

41 o,SayWorld在多核CPU上是同时运行的,我的环境变量设置为了8,输出结果也 oWorld!,没有出 NumCPU()返回CPU核数,NumGoroutine()返回当前进程的Goroutine线程数。即便我们没有开启新goroutine,NumGoroutine()也是2,这是因为除了主线程main,go还会启动一个GCHeap用来对内存进行管理及回收。importimport)func{fmt.Println(runtime.NumCPU())}CPUI7,四核,NumCPU(),返回的结果8,因in的超线程技术,可以在一个实体处理器中,运行两个逻辑线程(具体可以去一下超线程)。虽然我是四核,但是有8个逻辑内核。runtime.Goexit()函数用于终止当前goroutine,但defer函数将会被继续调用import()func{defer{fmt.Println("infori:=0;i<10;{fmt.Println(i)ifi>5{}}}func{gotest()varstrstringGOMAXPROCS(nint) 用来设置可同时运行的线程数,并返回当前设置的值,如 将不会改变当前的设置。通常这样使用runtime.GOMAXPROCS(runtime.NumCPU()42importimport)func}运行程序,输出结果为4,在第一章中提到我的GOMAXPROCS环境变量设置值为4列。你可以从一个goroutine中向channel发送数据,在另一个goroutine中取出这个值。生费goroutine从channel中取出数据进行处理。import"fmt"funcproducer(cchan{deferclose(c)//关闭channelfori0i10;i++{c<-i//阻塞,直到数据被消费者取走后才能发送下一条数}}funcconsumer(c,fchan{forifv,ok:=<-c;ok //阻塞,直到生产者放入数据后继续取}else}}}funcmain()buf:=make(chan:=make(chan43)go) //等待数据接收完}consummerrangefuncfuncconsumer(c,fchan{forv:=rangec{}}funcproducer(cchan<-{funcproducer(cchan<-{deferclose(c)//关闭channelfori0i10i++{c<-i//阻塞,直到数据被消费者取走后才能发送下一条数}}funcconsumer(c<-chanint,fchan<-{forv:=rangec{}Channel可以是带缓冲的。 第二个参数做为缓冲长度来初始化一个带缓冲funcmain()c:=make(chanint,2)c<-1 fmt.Println(<-fmt.Println(<-c)//此时若再从c中取数据,将出现阻塞,运 }44如果有多个channel需 ,可以考虑用select,随机处理一个可用的channelfuncfuncfibonacci(c,quitchan{x,y:=0,for{casec<-x,y=y,x+ycase<-quit:}}}funcmain()c:=make(chanint)quit:=make(chanint)gofunc(){fori:=0;i<10;}quit<-}当一channelread/write阻塞时,会一直被阻塞下去channel关闭,产生一个异常退出程序。Channel内部没有超时的定时器。但我们可以用select来实现channel的超时机importimport)funcmain()select{case<-c://因为没有向c发送数据,所以会一直阻fmt.Print("收到数据}}45进程同 共享资源。Go中的互syncimport()typeMyMapstruct mutex*sync.Mutex}func(this*MyMap)Get(keystring)(int,{thismutex.Lock()i,ok:=this.mp[key]thismutex.Unlock()if!ok{}returni,}func(this*MyMap)Set(keystring,v{thisthismp[key]=v}{thisfork,v:=range{fmt.Println(k,"=",}}{vara46}a=fori:=0;i<10;{m.Set(string(a+rune(i)),}}funcmain()m&MyMap{mp:make(map[string]intmutex:new(sync.Mutex)}goSetValue(m)/*启动一个线程向map写入值*/虽然我们赋值时,是按123进行的,但多次运行的结果,发现,map的顺序是不一样,因map是无序的。只要在读的时候不要有写的线程。这就是读写锁。读写锁充许多个线程同时读,所以并发性更好。读写锁具有以下特性:多个读操作可以同时写必须互斥,不充许两个写操作同时进行,也不能读、写操作同时进行写优先于读。在当前线程以读模式加锁后,其它线程进行读模式加锁,可以获得读的权限;在当前线程以读模式加锁后,其它线程加写锁时,将会堵塞,并且后继的读锁将会堵塞。这样可以避免读模式锁长期占用,导致写操作一直阻塞.下面的例子可以改成读写锁的实typetypeMyMapstruct }func(this*MyMap)Get(keystring)(int,{this.mutex.RLock()i,ok:=this.mp[key]if!ok{}returni,}{fork,v:=rangethismp{47}}}}funcmain()}48日期的获取与计import)func{}Time包定义了所有时间import)func{}Format函数把一个时间格式化为字符串,定义格式为func(tTime)Format(layoutstring),layoutGo2006代表年,01代表月,02代表日,15代表时,04代表分,05代表秒。不知2006-01-02是什么重要的日子,Go要用这种格式表。importimport)funcmain()fmt.Println(time.Now().Format("2006-01- }funcParse(layout,valuestring)(Time,error),用来把一个字符串转换成04代表分,05代表秒。importimport)funcmain()d,err:=time.Parse("01-02-2006","06-17-iferr!={}}49import)funcmain()t:=t2:=t.Add(import)funcmain()t:=t2:=t.Add(24*time.Hour)//当前时间加24小时,即明天的这个时间d:=t2.Sub(t) ift.Before(t2//tt2}}}}中。与NewTimer等价importimport)func{t:=<-ct=<-tm.C }50函数f是不带任何参数和返回值的。}func{fmt.Println(time.Now())}func{fmt.Println(time.Now())varstrstringfScan(r) *这里主要是等待用户输入,不让进程结束,进程结束定时器也就无效了。}import)import)funcmain()c:=time.Tick(10*import)funcmain()c:=time.Tick(10*time.Second)fort:=rangec{}}51import)func{import)func{ fmt.Println(path.Base("C:\\Windows"))/*无法识别Windows路径分隔符,将会把C:\\Windows做为一个路径*/fmt.Println(path.Base(strings.Replace("C:\\Windows","\\",-1)))/*把\转换成}import)func{fmt.Println(path.Clean("/a/b/../././c"))}路path包中封装了一些路径相关的操作,在开始接触文件操作之前,我们先看看路径Linux中,路径的格式为/user/bin路径中的分隔符是/;Windos中的路径格式C:\Windows路径中的分隔符是\。而Go中,只认/,不知道怎么回事,也许是对Windows的支持不够好。所以要想能够正常的使用path中的函数需要把\转换成/。在上面的例子中,我们使strings.Replace对路径分隔符进行了转换,最后一个参数,用来指定替换次数,-1表示替换所有。funcClean(pathstringstringpath等价的短路径。一般在路径中出现./或../时path.Clean("/a/b/../c")的结果为/a/cfuncDir(pathstring)string返回路径中 部分。也就是最后一个/前面的部分52importimport)func{/*/a/c/d*/importimport)funcmain() }funcIsAbs(pathstringbool用来判断路径是否绝对路径。在Linux下如果路径是以/开头的是绝对路径,如/user/bin,否则是相对路径;Windows下,以盘符开头的是绝对路径,如C:\Windows\system,而Windows\system是相对路径。importimport)func{fmt.Println(path.IsAbs("/a/b/c"))"\\""/"-1/*Go}上面代码,第一个输出true/a/b/c是绝对路径;第二个输出falseC:\Windows\system是绝对路径,但Go好像只支持Linux格式的路径,所以此处判断错误funcJoin(elem...string)string,用来进行路径的连接。a/bca/b/cimportimport)funcmain()fmt.Println(path.Join("a/b",53} import()funcmain()fmt.Println(path.Split("/a/b/test.txt"))/*/a/b/test.txt*/ /*/a/b/c/*/}在上面的例子中path.Split("/a/b/c/")只有 funcAbs(pathstringstringerror)用来把相对路径转换成绝对路径,该函数位于path/filepath包中。importimport)func{}funcWalk(rootstring,walkFnWalkFuncerrorroot下的所有文件和子目error,path为当前文件或文件侠的完整路径,info是os.FileInfo结构的表示。importimport)funcDispFile(pathstring,infoos.FileInfo,errerror) info.IsDir())return}func{filepath.Walk(".",54文件读在io包中提供了一些文件操作的函funcCreate(namestring)(file*File,err创建新文件,如果文件已存在,将被截断。新建的文件是可读写的。默认权0666(Linux下文件的权限设置格式)funcOpen(namestring)(file*File,err打开已经存在的文件,用 文件内容。 打开的文件是只读的。不能写funcOpenFile(namestring,flagint,permFileMode)(file*File,errOpenFile是一个通用的函数,可以用来创建文件,以只读方式打开文件,以读写方式打flag可以是下面的取值: 当文件不存在时创建文件 与O_CREATE一起使用,当文件已经存在时Open操作失败O_SYNC 以同步方式打开文件。每次wrie系统调用后都等待实际的物理I/O完成后才返回,默认(不使用该标记)是使用缓冲的,也就是说每次的写操作是写到系统内核缓冲区中,等系统缓冲区满后才写到实际设备。 如果文件已存在,打开时将会清空文件内容。必须于O_WRONLYFileMode参数是文件的权限,只有在文件不存在,新建文件时该参数才有效。用来指定新建的文件的权了。必须跟O_CREATE配合使用。importimport)funcmain() os.OpenFile("D:新建文本文档.txt",os.O_RDONLY|os.O_APPEND|os.O_CREATE,0666)iferr!=}buf:=make([]byte,1024)varstr55f.Seek(0,os.SEEK_SET)forn,ferr:=ifferr!=nil&&ferr!=}ifn==0}str+=}}上面的例子中,写完文件后,要想文件内容,需要调Seek重置文件指针,否则是内容需要重置文件指针开文件的开头儿。func(f*File)Seek(offsetint64,whenceint)(retint64,errSeek用来设置文件指针的位置,offet是偏移量,whence的取值可以是下面的三个 offset是相对文件开始位置的偏移量。 offset是相对文件指针当前位置的偏移量。 offset是相对文末尾的偏移量。ioutilIOr中所有内容。在上面的例子f.Read循环文件中使用ReadAll来代替,使代码变得简单。importimport)funcmain() os.OpenFile("D:新建文本文档.txt",os.O_RDONLY|os.O_APPEND|os.O_CREATE,0666)iferr!={56}}deferbuf,err1:=ioutil.ReadAll(f)iferr1!=nil{}}ReadAll简化了IO操作,但 文件内容还有更简单的方法,看下面这个函数import)funcmain()buf,errioutil.ReadFile("D:\\新建文本文档.txt")import)funcmain()buf,errioutil.ReadFile("D:\\新建文本文档.txt")iferr!=nil{}}向文件中写数据,如果文件不存在,将以perm权限创建文件importimport)funcmain()err:=ioutil.WriteFile("e:\\a.txt",[]byte("abcdefg"),0777)iferr!=nil{}else}}57遍 下的文 信息,位于OS包中。定义如下func(f*File)Readdir(nint)(fi[]FileInfo,errn>0最多返回n个文件。如个小于等零,返回importimport)funcmain()f,err:=os.OpenFile("C:\\Windows",os.O_RDONLY,0666)iferr!=nil{}arrFile,err1:=f.Readdir(0)iferr1!=nil{}fork,v:=rangearrFilefmt.Println(k,"\t",v.Name(),"\t",}}供了一个ReadDir函数,定义如下:下所有的文件和 。是对File.Readdir的封装importimport)funcmain()arrFile,err:=ioutil.ReadDir("C:\\Windows")iferr!=nil{}58forfork,v:=rangearrFilefmt.Println(k,"\t",v.Name(),"\t",}}序列化就是将对象的状态信息转换为可以或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性区。之后,可以通过从区中或反序列化对象在本节中我们仅介绍gob,列化的方法,xml,json的序列化将放在下一章中讨论Gob是o中所特有的序列化技术,它支持除inerfacefunctionchannel外的所有o数据类型。序列化使用Encoder,反序列化使用Decoder。我们可以把一个结构体序列化到文件中。然后再反序列化。f,f,err:=os.Create("data.dat")iferr!=nil{}deferencode:=f.Seek(0,os.SEEK_SET)decodergob.NewDecoder(f)vars1Student",import)typeStudent{NamestringAgeint}funcmain()s:=59}}608JSONXML解XML序列化与解XmlWebService中XML将数据SOAP消息。很多接XML来传递数据。Go中提XML序列化的文法,位于encoding/xml包中。funcenc*EncoderEncode(vinterfaceerror可以把一个对像直接序列化到io.Writer对import()typeStudent{NamestringAgeint}funcmain()f,err:=os.Create("data.dat")iferr!=nil{}defers:=s:=",encoder:=f.Seek(0,os.SEEK_SET)decoderxml.NewDecoder(f)vars1Student}61用记事本打开data.dat,可以看到文件内容是一段XML,如下 xml包的Marshal函数可以把一个对像直接序列化成字符importimport)typeStudent{NamestringAgeint}funcmain()s:=s:=",result,result,err:=iferr!=nil{}}上面的程序结 UnMarshal可以将一个xml反序列化为对varvarsStudent}import)typeStudent{NamestringAgeint}funcmain()str:=`<?xmlversion="1.0"encoding="utf-62import)typeStudentstructXMLNamexml.Nameimport)typeStudentstructXMLNamexml.Name`xml:"student"` }typeABCstringfuncmain(){str:=`<?xmlversion="1.0"encoding="utf- varsStudent}XMLNamexml.Name`xml:"student"`指定该结构体对应的xml标记,[]string[]stringimportimport)typeStudentstructXMLNamexml.Name}}typeABCstringfuncmain(){str:=`<?xmlversion="1.0"encoding="utf-<student "63varsStudent}除上面的方法,xml包还提供了其它的解析xml的文法。在.netjava中都提供三张encoding=\"utf-packageimport)typeStudent{NamestringAgeint三张encoding=\"utf-packageimport)typeStudent{NamestringAgeint}funcmain() decoder:=decoder:=varstrNamestringfor{token,err:=decoder.Token()iferr!=nil{}switcht:={case m:=xml.Star fmt.Println("Start",s strName= casecaseendelem:=xml.EndElement(t)fmt.Println("End",endelem.Name.Local)casedata:=64:",:":",:",casestr:=string(data)switchstrName{casefmt.Println("other:"fmt.Println("other:",}}}}JSONon是一种比XML更轻量级的数据交换格式,易于人们阅读和编写,也易于程序解析和生成。是较理想的、跨平台的、跨语言的数据交换语言,应用十分广泛。o提供了对on解/s:=s:= ",encoder:=import()typeStudent{NamestringAgeint}funcmain()f,err:=os.Create("data.dat")iferr!=nil{}65f.Seek(f.Seek(0,os.SEEK_SET)decoderjson.NewDecoder(f)vars1Student}指定解/编码时对应的json名称。import()typeStudentstructNamestring`json:"userName"`Ageint}funcmain()s:=s:= ",buf,err:=json.Marshal(s)iferr!=nil{}vars1Studentjson.Unmarshal(buf,&s1)}}程序运行结果为 66importimport)funcmain()str:=varvarmjson.Unmarshal([]byte(str),&m)fork,v:=rangem{switch{casecasestring:fmt.Println(k,}}} 值,Json中的所有数字(整型,浮点型)将被解析为float64,Json中的string,被解析为string类型,Json中的数组被解析为interface{}数组,Json中的空值解为67gogo9MySQLgogoMySQL驱database/driver定义了一些标准的接口,这些接口由具体的数据库驱动程序实现,Go 实现。database/sql包提供了一些通用的方法,这里的函数,多是调用了database/driver接口来实现的。接下来我们介绍几个常用的数据操作的函数。这里我们仅以Mysql为例,在开始之前,我们要先安装Mysql的驱动程序。我用的是 MySQL数据库操作下面我们介绍一下database/sql包的几个常用函根据driverName打开指定的数据库。driverName驱动的名称,dataSourceName通常包含了数据库名,和连接信息,如服务器地址、用户名、。不同的驱动程序,会有不同的func(db*DB)Exec(querystring,erface{})(Result,执行一个SQL查询,不返回任何行。通常用来执行数据的插入,更新操作。query是要执行的SQL语句,args是参数。执行成功errornil。Result是一个接口,定义如下:typeResult{LastInsertId()(int64,error)} 返回最后一次自动长列的值。RowsAffected,返回所影响的行func(db*DB)Query(querystring,erface{})(*Rows,Rows用来返回的数据,相当于.NetSqlDataReader,需要在数据库连接的情况下读数据。也就是在我们关闭连接前数据。func(r*Row)Scan(erface{})用来从返回的数据中,取数据varvaridvarnamestring68func(db*DB)QueryRow(querystring,erface{})下面我们新建一张表,然后进行插入 操作。表结构如下CREATETABLE`name`varchar(255)DEFAULTPRIMARYKEY(`id`) DEFAULT`packageimportpackageimport_/go-sql-)funcmain() iferr!=r)return}defervarresult values(?,?,?)"," ",19,true)iferr!=r)return}lastId,_:=result.LastInsertId()varrow*sql.Rowvarnamerow=db.QueryRow(varname69packagepackageimport_/go-sql-varid,ageintvarisBoyboolerr=row.Scan(&id,&name,&age,iferr!=}fmt.Println(id,"\t",name,"\t",age,"\t", iferr!=iferr!=nil{}for{varnamestringvarid,ageintvarisBoyboolrows.Scan(&id,&name,&age,fmt.Println(id,"\t",name,"\t",age,"\t",}varrows",18,rows,err=db.Query("select*db.Exec(db.Exec("truncate注意,Query返回的rows,取完数据后需要调用Close来释放资源func(db*DB)Prepare(querystring)(*Stmt,10000Prepare了,可70)iferr!=}deferdb.Close()varsmt*sql.Stmt iferr!=}fmt.Println("开始插入数据....",r:=rand.New(rand.NewSource(time.Now().UnixNano()))fori:=0;i<10000;i++{_,errsmt.Exec(fmt.Sprintf(%d"r.Int()),r.Intn(50),iferr!={)}}fmt.Println("数据插入完成!",插入一万条,我这里用了22分钟, 事 func(db*DB)Begin()(*Tx,开始一个事务71func(tx*Tx)Commit()提交事务func(tx*Tx)Rollback()用来回滚一个事务。Tx还有Query,QueryRow,,Prepare,Exec,这些用法跟DB对像的样importimport_/go-sql-})funcmain()iferr!=nil}deferdb.Close()vartrans*sql.Txtrans,err=db.Begin()iferr!=nil{} trans.Rollback事务insert插入的数据不会真正写到数据里。改成mit()可以将数据写入到数据库中。通常是根据SQL执行的结果,来判断iferr!={}else}(name,age,IsBoy)values('_,err=72limitpackageimportlimitpackageimport_ /go-sql-)funcmain()db,err:=sql.Open("mysql",iferr!=nil}defervarrowrow,row,err=db.Query("select*//SCanvarid,name,age,isBoyerr=row.Scan(&id,&name,&age,iferr!=nil}fmt.Println("id",id,fmt.Println("name",name,}}}[idnameageIsBoy]<nil>id[49]1name[229188160228184[idnameageIsBoy]<nil>id[49]1name[22918816022818473样看来,列名,可以跟Scan里的值一一对应了。按列名返回字段值,应该可以Scanfuncfunc(rs*Rows)Scan(erface{})errorfori,sv:=rangers.lastcolsiferr!=nil{returnfmt.Errorf("sql:Scanerroroncolumnindex%d:%v",i,}}return}Rs.ascols是当前行,所有列的值。Dest是我们传进来的参数。在26节中我们 ,变参其实就是一个slce,dest就是一个slce,里面存放的是我们传进来的参数。convertAssgn用来把列中的数据转换成我们需要的类型。该函数位于go\rc\pkgdatabaesqlconvertgo文我们有两种实现方式,一种是直接改daabae/sql包中的Rows给一数另在自己创建的程序中进行一层封装,来提供这个功能。我们用后一种实现方,通常情况下,不要改系统文件。下面我们实现一个自己的Rows结构体。}valuesmap用来存放当前行的数据,key是列名,区分大小写;value是列对应的数据columnName是列名。接下来我们重写Next函数,在这里把每列的数据取出来放到map中funcfunc(this*MyRows)Next(){bResult:=this.Rows.Next()ifbResult{//如果成功,取所有列的数据到valuesifthis.columnName==nil||len(this.columnName)=={this.columnName,_=}ifthis.values==nilthis.values=}vararrfori:=0;i<len(this.col

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论