编译语言:需要解释器,比如jvm,解释称二进制,然后处理器才能执行解释性:编译后为二进制语言
C、C++、Python、PHP/java/javascript等都是2005之前的,2005年出现了多核处理器,go在2007年出现,go天生支持并发
go语言特点:
应用范围:
腾讯内部go语言:蓝鲸、微服务架构TarsGo、云平台
知乎go语言重构
docker,k8s
...
Go语言使用场景:
go语言规定src,pkg,bin...目录统称为工作区,其中src目录必须手动创建,
如果是vscode安装,安装插件(1、chinses(中文),2、go安装)
注意:在哪个目录下执行gobuildmain.go,在哪个录下生成main.exe
gobuild-ohello.exe默认编译当前目录
goinstall安装
1、编译得到一个可执行文件2、拷贝到GOATH/bin1.2、跨平台编译main函数是程序的入口文件函数外(不在func内)只能放置标识符(变量、常量、)
默认我们gobuild的可执行文件都是当前操作系统可执行的文件,如果我想在windows下编译一个linux下可执行文件,那需要怎么做呢?
只需要指定目标操作系统的平台和处理器架构即可:
SETCGO_ENABLED=0//禁用CGOSETGOOS=linux//目标平台是linuxSETGOARCH=amd64//目标处理器架构是amd64使用了cgo的代码是不支持跨平台编译的
然后再执行gobuild命令,得到的就是能够在Linux平台运行的可执行文件了。
Mac下编译Linux和Windows平台64位可执行程序:
CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildLinux下编译Mac和Windows平台64位可执行程序:
CGO_ENABLED=0GOOS=darwinGOARCH=amd64gobuildCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildWindows下编译Mac平台64位可执行程序:
SETCGO_ENABLED=0SETGOOS=darwinSETGOARCH=amd64gobuild现在,开启你的Go语言学习之旅吧。人生苦短,let’sGo.
注意:在目录内执行gobuild,会生成一个不带exe,文件名为目录名的二进制可执行文件
Go1.14版本之后,都推荐使用gomod模式来管理依赖环境了,也不再强制我们把代码必须写在GOPATH下面的src目录了,你可以在你电脑的任意位置编写go代码。(网上有些教程适用于1.11版本之前。)
可以执行下面的命令修改GOPROXY:
创建目录:D:\Program_language\PRD\PG1并添加系统环境变量:GOPATH=D:\Program_language\PRD\PG1GOBIN=D:\Program_language\PRD\bin1.4、liteIde快捷键ctrl+{|}#快速到函数头部或尾部ctrl+/#选中范围进行注释ctrl+shift+i#鼠标放在对应的函数上,提示函数参数信息ctrl+alt+i#代码格式化二、数据类型2.1、变量和常量标识符:(程序中具有特殊意义的词,比如常量名、变量名、函数名等)标识符命名规范:字母、数字、下划线,只能以字母和下划线开头
25个关键字:
breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar37个保留字:
Constants:truefalseiotanilTypes:intint8int16int32int64uintuint8uint16uint32uint64uintptrfloat32float64complex128complex64boolbyterunestringerrorFunctions:makelencapnewappendcopyclosedeletecomplexrealimagpanicrecover变量的来历:程序运行过程中的数据是放在内存中的,利用变量在保存内存地址,使用方便
变量的类型:go语言中每个变量都有属于自己的类型,并且变量必须初始化后才能被使用
变量初始化:
常量:
注释://单行注释
/*多行注释*/变量交换
funcmain(){ stu1,stu2:=10,20 fmt.Println(stu1,stu2) //1、变量交换 stu1=stu2 fmt.Println(stu1,stu2)//2020结果 stu2=stu1 fmt.Println(stu1,stu2)//2020结果 //2、变量交换-中间变量 stu1,stu2=10,20 fmt.Println(stu1,stu2)//1020 c:=stu1 stu1=stu2 stu2=c fmt.Println(stu1,stu2)//2010 //2、变量交换-方式3 stu1,stu2=10,20 stu1,stu2=stu2,stu1 fmt.Println(stu1,stu2)//2010}2.2、iotaiota是go的常量计数器,只能在常量的表达式中使用
const( a=100 b=200 c=iota) fmt.Println(a,b,c)//100,200,2 const( d=iota e=100 f=iota ) fmt.Println(d,e,f)//0,100,2 const( m1,m2=iota+1,iota+2//iota为0 m3,m4=iota+1,iota+2//iota为1 ) fmt.Println(m1,m2,m3,m4)//1,2,2,3 const( _=iota KB=1<<(10*iota)//iotao为1,1左移10位 MB=1<<(10*iota) GB=1<<(10*iota) TB=1<<(10*iota) PB=1<<(10*iota) ) fmt.Println(KB,MB,GB,TB,PB) /* 10241048576107374182410995116277761125899906842624 */容量单位:
整形:
其他数字类型:
浮点型和复数:
v:=0b00101101,代表二进制的101101,相当于十进制的45。v:=0o377,代表八进制的377,相当于十进制的255。v:=0x1p-2,代表十六进制的1除以22,也就是0.25。而且还允许我们用_来分隔数字,比如说:
v:=123_456等于123456。
packagemainimport"fmt"funcmain(){ //十进制 varaint=10 fmt.Printf("%d\n",a)//10 fmt.Printf("%b\n",a)//1010占位符%b表示二进制 //八进制以0o开头 varbint=077 fmt.Printf("%o\n",b)//77 //十六进制以0x开头 varcint=0xff fmt.Printf("%x\n",c)//ff fmt.Printf("%X\n",c)//FF}2.6、八进制&十进制go语言中无法直接定义二进制
funcstr(){ path1:="D:\\Golang\\project\\project1\\src" path2:=`D:\Golang\project\project1\src`//path1和path2效果一样 issue:=`床前明月光疑是地上霜举头望明月低头思故乡` fmt.Println(path1) fmt.Println(path2) fmt.Println(issue)}字符串的常用操作
funcstr2(){a1:="hello"a2:="world"//info:=a1+a2拼接方法1//fmt.Println(info)fmt.Printf("%s%s",a1,a2)ss1:=fmt.Sprintf("%s%s",a1,a2)//拼接方法2,返回结果为一个字符串fmt.Println(ss1)//分割ret:=strings.Split(ss1,"o")//以"o"为分隔符[hellwrld]fmt.Println(ret)//拼接fmt.Println(strings.Join(ret,"+"))//hell+w+rld//包含判断fmt.Println(strings.Contains(ss1,"world"))//truefmt.Println(strings.Contains(ss1,"Hello"))//false//前缀,后缀fmt.Println(strings.HasPrefix(ss1,"hello"))//truefmt.Println(strings.HasSuffix(ss1,"hello")) //falsess2:="aboddbc2oa"//子串出现的位置fmt.Println(strings.Index(ss2,"o"))//2fmt.Println(strings.LastIndex(ss2,"o"))//8}修改字符串
字符串:不可变类型修改字符串,需要先将其转换成[]rune,或者[]byte,完成后在转换string,无论哪种转换,都会重新分配内存。无论哪种转换,都会重新分配内存,并复制字节数组。
其他类型转换成字符串
funcstr1(){ str:="helloworld" slice:=[]byte(str) fmt.Println(slice)//[10410110810811132119111114108100]} funcstr2(){ str:=[]byte{'h','e','l','l','0',97} fmt.Println(str)//[1041011081084897] fmt.Println(string(str))//hell0a}funcstr3(){//将其他类型转换为字符串format系列函数 str:=strconv.FormatInt(120,8) fmt.Println(str)//170,120转换成8禁止120%8=15余0;15%8=1余7 //funcFormatFloat(ffloat64,fmtbyte,prec,bitSizeint)string//fmt打印方式,prec:小数位保留个数,bitSize:float32还是float64 s2:=3.1415926 s3:=strconv.FormatFloat(s2,'f',4,32) fmt.Printf("%T\t%v\n",s3,s3)//string 3.1416}字符串转换为其他类型
funcstr4(){ a,b,c:="3.14","true","12" fmt.Printf("%T\t%T\t%T\n",a,b,c)//string string string a1,err:=strconv.ParseInt(a,10,32)//转换为int;数字是什么进制表示的,要存储为int32/int64 iferr!=nil{ fmt.Println(err)//parsing"3.14":invalidsyntax } a2,_:=strconv.ParseFloat(a,32)///期望的接受类型为float32,具体以实际为准 a3,err:=strconv.ParseInt(c,16,32)//c"12"为16进制,转换为int32 b1,_:=strconv.ParseBool(b) fmt.Printf("%T\t%T\t%T\t%T\n",a1,a2,a3,b1)//int64 float64 int64 bool fmt.Println(a1,a2,a3,b1)//03.1418true}其他类型字符串
vara:='中'varb:='x'Go语言的字符有以下两种:
当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32。
Go使用了特殊的rune类型来处理Unicode,让基于Unicode的文本处理更为方便,也可以使用byte型进行默认字符串处理,性能和扩展性都有照顾。
//遍历字符串functraversalString(){	s:="hello沙河"	fori:=0;i 104(h)101(e)108(l)108(l)111(o)230()178(2)153()230()178(2)179(3)104(h)101(e)108(l)108(l)111(o)27801(沙)27827(河)因为UTF8编码下一个中文汉字由3~4个字节组成,所以我们不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。 字符串底层是一个byte数组,所以可以和[]byte类型相互转换。字符串是不能修改的字符串是由byte字节组成,所以字符串的长度是byte字节的长度。rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成。 程序的三大结构:顺序结构、选择结构、循环结构 用法 ifcondition{//单条件判断//dosomething}ifcondition{//多条件判断}elseifcondition{//dosomething}else{//dosomething}if单条件先跟个语句然后再做条件判断 ifstatement;condition{//单条件,带语句的判断//dosomething}funcmain(){////多条件,不带语句的判断	ifnum:=34,num<=50{//num的作用域只在if语句内有效		fmt.Println("Numberislessthen50")	}elseifnum>=51&&num<=100{		fmt.Println("Thenumberisbetween51and1100")	}else{		fmt.Println("Thenumberisgreaterthan100")	}		fmt.Println(num)//会失败,因为num的作用域只在if范围内有效}3.2、forgo只有for没有while循环for循环可以通过break,goto,return,panic语句强制退出循环go语言中forrange遍历数组、切片、字符串、map以及通道(channel)。通过forrange遍历的返回值有一下规律:1、数组、切片、字符串返回索引和值2、map返回键和值3、通道只返回通道内的值for初始语句;条件表达式;结束语句{//格式1循环体}for;条件表达式;结束语句{//格式2循环体}for条件表达式{//格式3循环体}for{//格式4,死循环循环体语句}示例1 funcstr6(){	info:="hello中国"	fori,v:=rangeinfo{		//fmt.Println(i,v)//默认输出的是utf-8的编自值		fmt.Printf("%v%s\n",i,string(v))		//fmt.Printf("%d%c\n",i,v)//效果同上,打印字符	}}示例2 funcbreak1(){	fori:=0;i<10;i++{		ifi==3{			continue//跳过本次循环		}		ifi==5{			break//跳出for循环		}		fmt.Println(i)	}	fmt.Println("over")}99乘法表 funca99(){	fori:=1;i<=9;i++{		forv:=1;v<=i;v++{			fmt.Printf("%d*%d=%v\t",i,v,i*v)		}		fmt.Println()	}}3.3、switch简化大量的判断和一个具体的值进行判断 funcgoDemo1(){	varbreakFlagbool	fori:=0;i<10;i++{		forv:=0;v<=10;v++{			ifv==3{				breakFlag=true				break//1个break只跳出一层循环			}			fmt.Println(i,v)		}		ifbreakFlag{//如果breakFlag为ture,就跳出外层循环			break		}	}}funcgoDemo2(){//优化后的go代码	fori:=0;i<10;i++{		forv:=0;v<=10;v++{			ifv==3{				gotobreakFlag//跳转到指定label			}			fmt.Println(i,v)		}	}breakFlag://label名称	fmt.Println("GameOver...!")}3.5、break和continue跳转语句:go和breakbreak语句可以结束for、switch和select的代码块。 break语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的for、switch和select的代码块上。举个例子: funcbreakDemo1(){BREAKDEMO1:	fori:=0;i<10;i++{		forj:=0;j<10;j++{			ifj==2{				breakBREAKDEMO1			}			fmt.Printf("%v-%v\n",i,j)		}	}	fmt.Println("...")}continue语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for循环内使用。 在continue语句后添加标签时,表示继续执行开始标签对应的循环的下一次循环。例如: #1、不加labelfuncmain(){	forx:=1;x<=9;x++{		fory:=x;y<=9;y++{			ify==2{				continue			}			fmt.Printf("%d*%d=%d\t",x,y,x*y)		}		fmt.Println()	}}D:\Program_language\PRD\PG1\src>gorunmain.go1*1=11*3=31*4=41*5=51*6=61*7=71*8=81*9=92*3=62*4=82*5=102*6=122*7=142*8=162*9=183*3=93*4=123*5=153*6=183*7=213*8=243*9=274*4=164*5=204*6=244*7=284*8=324*9=365*5=255*6=305*7=355*8=405*9=456*6=366*7=426*8=486*9=547*7=497*8=567*9=638*8=648*9=729*9=81#2、加上labelfuncmain(){test:	forx:=1;x<=9;x++{		fory:=x;y<=9;y++{			ify==2{				continuetest			}			fmt.Printf("%d*%d=%d\t",x,y,x*y)		}		fmt.Println()	}}D:\Program_language\PRD\PG1\src>gorunmain.go#continue到外层循环1*1=13*3=93*4=123*5=153*6=183*7=213*8=243*9=274*4=164*5=204*6=244*7=284*8=324*9=365*5=255*6=305*7=355*8=405*9=456*6=366*7=426*8=486*9=547*7=497*8=567*9=638*8=648*9=729*9=81四、高级数据类型4.1、运算符go语言内置的运算符: 位运算符: 定义数组 初始化 注意:多维数组只有第一层可以使用...来让编译器推导数组长度。例如: packagemainimport(	"fmt"	"math/rand"	"time")/*1、定义const,确定取值范围2、定义每个const的随机取值3、随机在每个const中取出随机的值*/const(	NUmStr="0123456789"	CharStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"	SpecStr="+=-@#~,.[]()!%^*$")funcranSecret(nint){	rand.Seed(time.Now().UnixNano())	varranS=func()int{		returnrand.Intn(3)	}	varranNum=func()string{		returnstring(NUmStr[rand.Intn(len(NUmStr))])	}	varranChart=func()string{		returnstring(CharStr[rand.Intn(len(CharStr))])	}	varranSpec=func()string{		returnstring(SpecStr[rand.Intn(len(SpecStr))])	}	//varsecret[n]string//non-constantarraybound,go数组的长度必须是常量,不能是变量	secret:=make([]string,n)	fori:=0;i 切片的三要素:指针、长度、容量//指针:切片第一个元素 切片不能直接比较,切片唯一合法的比较操作是:nil一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例: 一个nil的底层切片是切片是没有底层数组的,长度和容量都是0。但我们不能说长度和容量都是0的切片一定是nil(len(s)==0和s==nil是不一样的) 切片的扩容策略: funccopy1(){//copy目标slice未初始化,会copy失败,但是不会报错	a1:=[]int{1,2,3}	a2:=a1	a2[0]=100	vara3[]int	copy(a3,a1)	fmt.Println(a1)//[10023]	fmt.Println(a2)//[10023]	fmt.Println(a3==nil)//true	fmt.Println(a3)//[]因为a3是nil,需要对a3初始化后才能使用。}funccopy2(){//copy目标slice初始化但是len为0,仍会copy失败	a1:=[]int{1,2,3}	a2:=a1	a2[0]=100	vara3[]int	a3=make([]int,0,3)	copy(a3,a1)	fmt.Println(a1)//[10023]	fmt.Println(a2)//[10023]	fmt.Println(a3==nil)//false	fmt.Println(a3)//[],copy不会自动扩容切片,而a3的长度为0,因此拷贝不进去结果为[]}funccopy3(){	a1:=[]int{1,2,3}	a2:=a1	vara3[]int	a3=make([]int,3,3)	copy(a3,a1)	a2[0]=100	fmt.Println(a1)//[10023]	fmt.Println(a2)//[10023]	fmt.Println(a3==nil)//false	fmt.Println(a3)//[123]}4.3.2、切片传递数组参数传递为值传递,切片是引用传递,在被调函数内修改会修改原有切片值 funcslice4(a[]int){	a[0]=100//这里修改的是原slice的	a=append(a,4,5,6)	a[0]=200	fmt.Println(a)//[2002345456],这里不会修改原slice,因为slice进行了扩容,开辟了新的内存地址}funcmain(){	slice:=[]in{1,2,3,4,5}	slice4(slice)//传递的是地址,切片名本身就是地址	fmt.Println(slice)//[1002345]}4.3.3、make和newmake也是用来分配内存的,区别于new,他只用于slice、map以及chan的内存创建。而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种就是引用类型,所以他们没有必要返回他们的指针了。make函数的函数签名如下:funcmake(tType,size...IntegerType)Typemake函数是无可替代的,我们在使用slice、map和channel的时候。都需要使用make进行初始化,然后才可以对他们进行 make和new的区分: 代码示例1:会panic funcmk(){	vara*int//nil	*a=100//*a根据nil的内存地址找值,自然找不到	fmt.Println(a)//invalidmemoryaddressornilpointerdereference		varbmap[string]int	b["洛阳"]=379	fmt.Println(b)//panic:assignmenttoentryinnilmap}funcmk(){	vara1*int	fmt.Println(a1)//nil	vara2=new(int)	fmt.Println(a2)//0xc0000100c0	fmt.Println(*a2)//0	*a2=100	fmt.Println(*a2)//100	varb=make(map[string]int,5)	b["洛阳"]=379	fmt.Println(b)}4.4、指针(重点)4.4.1、指针参数传递涉及两个概念:值(存储在内存中的数据)和地址(数据所在内存的地址) go语言不存在指针操作(不能修改),只需要记住2个字符:1、&取出地址;2、*根据地址取值 funcmap1(){	varm1map[string]int	fmt.Println(m1==nil)//true,map为引用类型,需要先使用make进行初始化	m1["小明"]=18//panic:assignmenttoentryinnilmap	fmt.Println(m1)}funcmap1(){	varm1map[string]int	fmt.Println(m1==nil)//true	m1=make(map[string]int,5)//容量建议规划合理,避免需要多次扩容	fmt.Println(m1==nil)//false	m1["小明"]=18	fmt.Println(m1)//map[小明:18]		delete(m1,"小刚刚")//如果删除了一个不存在的,则什么都不会做		fmt.Println(m1["小刚刚"])//如果不存在,范围0或者空		v,ok:=m1["小刚"]//判断值是否存在	if!ok{		fmt.Println("小刚不存在")	}else{		fmt.Println(v)	}}godocbuiltin.delete funcs2(){	a1:="fuck"	a2:="dad"	a3:="abc"	a4:="Geook"	fmt.Println(a1,a2,a3,a4)	vars1=make([]string,10)	s1=append(s1,a1)	s1=append(s1,a2)	fork,v:=ranges1{		fmt.Println(k,v)//10fuck		11dad(0-9为s1的初始化大小),[]string在go中存储为key-value形式([int]string数组)	}	fmt.Println(s1[10])//fuck	fmt.Println(s1[11])	//dad}//根据key名称进行排序funcrand01(){	rand.Seed(time.Now().UnixNano())//初始化随机种子	varscoreMpa=make(map[string]int,200)	fori:=0;i<100;i++{		key:=fmt.Sprintf("stu%02d",i)//生成stu开头的字符串		value:=rand.Intn(100)//生成0-99的随机整数		scoreMpa[key]=value	}	//取出map中所有的key放到切片keys中	varkeys=make([]string,200)	forkey:=rangescoreMpa{		keys=append(keys,key)	}	sort.Strings(keys)//对字符型切片进行排序	for_,key:=rangekeys{		fmt.Println(key,scoreMpa[key])	}}map和slice组合: funcmap2(){	//1、元素类型为map的切片	vars1=make([]map[int]string,0,10)//对切片进行初始化,注意map并没有初始化,s1[0]表示的是s1切片的第一个元素(元素类型为map)	fmt.Println(s1[0]==nil)//true	s1[0][100]="A"	fmt.Println(s1)//runtimeerror:indexoutofrange,因为s1的第0个元素是没有的	}funcmap2(){	//1、元素类型为map的切片	vars1=make([]map[int]string,10,10)	fmt.Println(s1[0]==nil)//true	s1[0]=make(map[int]string,1)//对s1[0]初始化	fmt.Println(s1[0]==nil)//false	s1[0][100]="A"	fmt.Println(s1)//[map[100:A]map[]map[]map[]map[]map[]map[]map[]map[]map[]]	//2、值为切片类型的map	varm1=make(map[string][]int,10)	m1["北京"]=[]int{10,20,30}	m1["上海"]=[]int{11,21,31}	fmt.Println(m1)//map[北京:[102030]上海:[112131]]}练习题1:统计中文字符个数: funccount1(){//统计中文个数	info:="Hello中国我爱你!"	//1、依次获取到字符串的值	sum:=0;	for_,v:=rangeinfo{		//2、判断当前这个字符是不是汉字		ifunicode.Is(unicode.Han,v){			//3、统计和输出汉字			sum++		}	}	fmt.Printf("infois:%v\nnumof中文字符:%d\n",info,sum)}练习题2:统计单词出现的次数: funccount2(){//统计"howdoyoudo!"单词出现个数//1、字符串按照空格切割得到切片。2、遍历切片存储到一个map。3、累加出现的次数	info:="howdoyoudo"	m1:=strings.Split(info,"")//按照空格切割为一个切片	map1:=make(map[string]int,10)	for_,v:=rangem1{		map1[v]++	}	fori,k:=rangemap1{		fmt.Printf("Word:\"%v\",appear%dtimes.\n",i,k)	}}funccount3(){	info:="howdoyoudo"	m1:=strings.Split(info,"")	map1:=make(map[string]int,10)	for_,v:=rangem1{		//如果原来map中不存在w这个key那么出现次数为=1		if_,ok:=map1[v];!ok{			map1[v]=1		}else{			map1[v]++		}	}	fori,k:=rangemap1{		fmt.Printf("Word:\"%v\",appear%dtimes.\n",i,k)	}}练习题3:回文判断: 主函数作为程序的入口,是不允许有参数的 funcjiecheng(nuint8)uint8{	ifn<=1{		return1	}	returnn*jiecheng(n-1)}funcmain(){	ret:=jiecheng(5)	fmt.Println(ret)}n个台阶,一次可以走1步,也可以走2步,有多少种走法。 funcf11(ffunc()){//自己的	fmt.Println("thisisf11")	f()}funcf22(x,yint){//别人的	fmt.Println("thisisf22")	fmt.Println(x+y)}funcf33(x,yint)func(){	tmp:=func(){		fmt.Println(x,y)	}	returntmp}funcmain(){	//1、调用方法1	test:=f33(10,20)	f11(test)//thisisf11;1020	//2、调用方法2	f11(f33(10,20))//结果同上}练习2:文件名添加后缀 //思路1,每一个jpg,txt定义一个函数,这样就比较累赘//思路2,使用闭包,返回一个函数funcmakeSuffix(suffixstring)func(filestring)string{//入参为后缀(string),出参为函数	tmp:=func(filestring)string{//定义"func(filestring)string"返回函数		if!strings.HasSuffix(file,suffix){			returnfile+suffix		}else{			returnfile		}	}	returntmp}funcmain(){	jpgFunc:=makeSuffix(".jpg")//创建jpg函数	txtFunc:=makeSuffix(".txt")//创建txt函数	fmt.Println(jpgFunc("test"))	fmt.Println(jpgFunc("test.jpg"))	fmt.Println(txtFunc("test"))}练习3 funcBase(baseint)(func(int)int,func(int)int){	add:=func(xint)int{		base+=x		returnbase	}	sub:=func(yint)int{		base-=y		returnbase	}	returnadd,sub}funcmain(){	f1,f2:=Base(10)	fmt.Println(f1(1),f2(2))//11(10+1),9(11-2),调用f1(1)和f2(2)修改的都是公用的base,因此会影响。每一次只有在用到的时候才会去获取外部变量base的值	fmt.Println(f1(3),f2(4))//12(9+3),8(12-4)}5.4、fmt标准库fmt包实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。通用占位符: 布尔型: 浮点数和复数: 字符串和[]byte: 宽度标识符: 其他flag: Print 查找字符串/Index funcmain(){	//1、包含-regexp.matchString	str1:="helloworldworld!helloworld"	match,_:=regexp.MatchString("he(..)o",str1)	fmt.Println(match)//true	//2、1的增强版,regexp.Compie/regexp.matchString	r,_:=regexp.Compile("he(..)o")	fmt.Println(r.MatchString(str1))//true;str1中是否包含"he(..)o"结果为true	//3、返回匹配的子串-输出首次匹配结果/索引;r.FindString|r.FindStringIndex	tmp:=r.FindString(str1)	fmt.Println(tmp)//返回子串,hello	fmt.Println(string(r.Find([]byte(str1))))//hello,同上	//4、返回所有匹配的字串-r.FindAllString//FindAllString和FindAllStringSubmatch用的多	str2:="abccdefghacmaskanswerprequest"	r2,_:=regexp.Compile("a.")	tmp2:=r2.FindAllString(str2,-1)	fmt.Println(tmp2)//[abacasan]	tmp3:=r2.FindAllStringSubmatch(str2,-1)	fmt.Println(tmp3)//[[ab][ac][as][an]]	tmp4:=r2.FindAllSubmatch([]byte(str2),-1)	fmt.Println(tmp4)//[[[9798]][[9799]][[97115]][[97110]]]}替换 funcmain(){	str:="1,2,3,4,5"	tmp:=strings.Split(str,",")	fmt.Println(tmp)//[12345]	tmp=strings.SplitAfter(str,",")	fmt.Println(tmp)//[1,2,3,4,5]	tmp=strings.SplitAfterN(str,",",3)	fmt.Println(tmp,len(tmp))//[1,2,3,4,5]3;对'1,'和'2,'进行了切分,后面的是一块}
