你应该$HOME/.profile文件增加下面设置。
1搭建go的环境 2step1:去golang的官网下载go的安装包 3windows:go1.9.2.....msi 4mac:go1.9.2......pkg 5 双击傻瓜式安装 6linux:go1.9.2.linux-amd64.tar.gz 7 默认到下载目录下: 8 需要将压缩包解压到指定的目录下:/usr/local 9 命令:cd 下载 10 命令:sudo tar -xzf go1.9.2.linux-amd64.tar.gz -C /usr/local 11 12 配置环境变量: 13 GOROOT:安装的目录 14 bin目录:go命令 15 16 ** GOPATH:用户编写的go的源代码的位置 ** 17 src:源代码 18 pkg:编译后的包的位置 19 bin:可执行文件 20 21 默认在home/ruby/go 22 桌面,下载,文档。。。。。
==注意: GOPATH目录必须存在src、pkg、bin文件夹==
将goroot和gopath配置到ubuntu的配置文件中:$HOME/.profile
1cd进入$HOME, 2ls -a,查看文件,包含隐藏:.profile 3使用vim编辑.profile 4 a)vi .profile,打开该文件 5 b) 输入字母i,表示进入vim的编辑模式 6 c) 将goroot,gopath配置到文件中 7 export PATH=$PATH:/usr/local/go/bin 8 export GOPATH=$HOME/go 9 d) 保存文件并退出 10 点击esc,退出编辑模式 11 输入:wq,保存并 退出 12需要让配置好的.profile文件生效 13 a)进入到/usr/local目录 14 b)执行命令:source $HOME/.profile
访问go官网需要翻墙 https://golang.org/
Go语言最主要的特性:
- 自动垃圾回收
- 更丰富的内置类型
- 函数多返回值
- 错误处理
- 匿名函数和闭包
- 类型和接口
- 并发编程
- 反射
- 语言交互性
go的源码文件分三类 命令源码文件、库源码文件、测试源码文件 命令源码文件:
1声明自己属于main代码包、包含无参数声明和结果声明的main函数 2被安装后,相应的可执行文件会被存放到GOBIN指向的目录或<当前工作区目录>/bin下 3命令源码文件是Go程序的入口,但不建议把程序都写在一个文件中
注意:同一个代码包中强烈不建议直接包含多个命令源码文件
库源码文件:
1不具备命令源码文件的那两个特征的源码文件 2被安装后,相应的归档文件会被存放到<当前工作区目录>/pkg/<平台相关目录>下
测试源码文件:
1不具备命令源码文件的那两个特征的源码文件 2名称以_test.go为后缀 3其中至少有一个函数的名称以Test或Benchmark为前缀 4并且,该函数接收一个类型为*testing.T或*testing.B的参数 5func TestFind(t*testing.T){ 6 // 省略若干条语句 7} 8 9第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。 10 11下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。 12 13下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。 14 15下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。 16 17下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。 18使用 fmt.Print("hello, world\n") 可以得到相同的结果。 19Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。 20 21当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
导包方式import "包名"
import . "包名"不用给包名起别名,import 别名 "包名"
只做初始化源码文件使用import _ "包名"
也可以用路径指向包import "路径/包"可以是绝对路径或者相对路径
要导入多个包使用import ("包名1" "包名2")
go run 文件名,对go源码文件编译并执行
1-a:强制编译相关代码,不论他们的编译结果是否是最新的 2-n:打印编译过程中所需运行的命令,但不真正执行他们 3-p n:并行编译,其中n为并行的数量 4-v:列出被编译的代码包的名称。如果和-a连用会打印所有代码包1.3中的所有指包含Go语言自带的标准库的代码包,1.4中的所有不包含Go语言自带的标准包 5-work:显示编译时创建的临时工作目录的路径,并且不删除它 6-x:打印编译过程中所需运行的命令,并执行
go语言关键字| | | |
---|---|---|---|---
break | default | func | interface | select
case | defer | go | map | struct
chan | else | goto | package | switch
const | fallthrough | if | range | type
continue | for | import | return | var
标识符| | | | | | | | ---|---|---|---|---|---|---|---|--- append | bool | byte | cap | close | complex | complex64| complex128 | uint16 copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 int32 | int64 | iota | len | make | new | nil | panic | uint64 print | println | real | recover | string | true | uint | uint8| uintptr
| 占位符 | 含义 |
|---|---|
| %% | 一个%字面量 |
| %b | 一个二进制整数值(基数为2),或者是一个(高级的)用科学计数法表示的指数为2的浮点数 |
| %c | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
| %d | 一个十进制数值(基数为10) |
| %e | 以科学记数法e表示的浮点数或者复数值 |
| %E | 以科学记数法E表示的浮点数或者复数值 |
| %f | 以标准记数法表示的浮点数或者复数值 |
| %g | 以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
| %G | 以%E或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
| %o | 一个以八进制表示的数字(基数为8) |
| %p | 以十六进制(基数为16)表示的一个值的地址,前缀为0x,字母使用小写的a-f表示 |
| %q | 使用Go语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字 |
| %s | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以’\0‘结尾,这个’\0’即空字符) |
| %t | 以true或者false输出的布尔值 |
| %T | 使用Go语法输出的值的类型 |
| %U | 一个用Unicode表示法表示的整型码点,默认值为4个数字字符 |
| %v | 使用默认格式输出的内置或者自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话 |
| %x | 以十六进制表示的整型值(基数为十六),数字a-f使用小写表示 |
| %X | 以十六进制表示的整型值(基数为十六),数字A-F使用小写表示 |
占位符 https://studygolang.com/articles/2644
1println是标准输出 2printf是占位符替换输出 "%d",10
Go 语言中变量的声明必须使用空格隔开,如:
1var age int; // ;可以省略 2age := 1;
无空格:
fruit = apples + oranges;
数据类型:
| 序号 | 类型和描述 |
|---|---|
| 1 | 布尔型<br>布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
| 2 | 数字类型<br>整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。 |
| 3 | 字符串类型<br>字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。 |
| 4 | 派生类型:<br>包括:<br>(a) 指针类型(Pointer)<br>(b) 数组类型<br>(c) 结构化类型(struct)<br>(d) Channel 类型<br>(e) 函数类型<br>(f) 切片类型<br>(g) 接口类型(interface)<br>(h) Map 类型 |
数字类型: 整型:
| 序号 | 类型和描述 |
|---|---|
| 1 | uint8<br>无符号 8 位整型 (0 到 255) |
| 2 | uint16<br>无符号 16 位整型 (0 到 65535) |
| 3 | uint32<br>无符号 32 位整型 (0 到 4294967295) |
| 4 | uint64<br>无符号 64 位整型 (0 到 18446744073709551615) |
| 5 | int8<br>有符号 8 位整型 (-128 到 127) |
| 6 | int16<br>有符号 16 位整型 (-32768 到 32767) |
| 7 | int32<br>有符号 32 位整型 (-2147483648 到 2147483647) |
| 8 | int64<br>有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型:
| 序号 | 类型和描述 |
|---|---|
| 1 | float32<br>IEEE-754 32位浮点型数 |
| 2 | float64<br>IEEE-754 64位浮点型数 |
| 3 | complex64<br>32 位实数和虚数 |
| 4 | complex128<br>64 位实数和虚数 |
其他更多的数字类型:
| 序号 | 类型和描述 |
|---|---|
| 1 | byte<br>类似 uint8 |
| 2 | rune<br>类似 int32 |
| 3 | uint<br>32 或 64 位 |
| 4 | int<br>与 uint 一样大小 |
| 5 | uintptr<br>无符号整型,用于存放一个指针 |
1//类型相同多个变量, 非全局变量 2var vname1, vname2, vname3 type 3var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要显示声明类型,自动推断 4 5vname1, vname2, vname3 := v1, v2, v3 //出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误 6//这种不带声明格式的只能在函数体中出现 7 8// 这种因式分解关键字的写法一般用于声明全局变量 9var ( 10 vname1 v_type1 11 vname2 v_type2 12)
==函数体内,如果有些变量不需要声明类型,单纯地赋值也是不够的,这个值必须被使用==
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
const identifier [type] = value
常量还可以用作枚举:
1const ( 2 Unknown = 0 3 Female = 1 4 Male = 2 5)
常量可以用len(), cap(),unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:
1package main 2 3import "unsafe" 4const ( 5 a = "abc" 6 b = len(a) 7 c = unsafe.Sizeof(a) 8) 9 10func main(){ 11 println(a, b, c) 12}
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
iota 可以被用作枚举值:
1const ( 2 a = iota 3 b = iota 4 c = iota 5)
1package main 2 3import "fmt" 4 5const ( 6 a = iota //0 7 b //1 8 c //2 9 d = "ha" //ha 10 e //ha 11 f = 100 //100 12 g //100 13 h = iota //7 14 i //8 15) 16 17func main() { 18 fmt.Println(a,b,c,d,e,f,g,h,i) 19}
算术运算符
| 运算符 | 描述 | 实例 |
|---|
- | 相加 | A + B 输出结果 30
- | 相减 | A - B 输出结果 -10
- | 相乘 | A * B 输出结果 200 / | 相除 | B / A 输出结果 2 % | 求余 | B % A 输出结果 0 ++ | 自增 | A++ 输出结果 11 -- | 自减 | A-- 输出结果 9
关系运算符
| 运算符 | 描述 | 实例 |
|---|---|---|
| == | 检查两个值是否相等,如果相等返回 True 否则返回 False。 | (A == B) 为 False |
| != | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 | (A != B) 为 True |
| 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 | (A > B) 为 False < | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 | (A < B) 为 True = | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 | (A >= B) 为 False <= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 | (A <= B) 为 True
逻辑运算符 运算符 | 描述 | 实例 && | 逻辑 AND 运算符。如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False || | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A || B) 为 True ! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True
位运算
| p | q | p & q | p | q | p ^ q |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
| 1 | 0 | 0 | 1 | 1 |
位运算符号
| 运算符 | 描述 | 实例 |
|---|---|---|
| & | 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | | 按位或运算符" | "是双目运算符。 其功能是参与运算的两数各对应的二进位相或 |
| ^ | 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
| << | 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 |
| 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111
赋值运算符
| 运算符 | 描述 | 实例 |
|---|---|---|
| = | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C |
| += | 相加后再赋值 | C += A 等于 C = C + A |
| -= | 相减后再赋值 | C -= A 等于 C = C - A |
| *= | 相乘后再赋值 | C *= A 等于 C = C * A |
| /= | 相除后再赋值 | C /= A 等于 C = C / A |
| %= | 求余后再赋值 | C %= A 等于 C = C % A |
| <<= | 左移后赋值 | C <<= 2 等于 C = C << 2 |
= | 右移后赋值 | C >>= 2 等于 C = C >> 2 &= | 按位与后赋值 | C &= 2 等于 C = C & 2 ^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |= | 按位或后赋值 | C |= 2 等于 C = C | 2
其他运算符
| 运算符 | 描述 | 实例 |
|---|---|---|
| & | 返回变量存储地址 | &a; 将给出变量的实际地址。 |
- | 指针变量。 | *a; 是一个指针变量
运算符优先级
| 优先级 | 运算符 |
|---|---|
| 7 | ^ ! |
| 6 | * / % << >> & &^ |
| 5 | + - | ^ |
| 4 | == != < <= >= > |
| 3 | <- |
| 2 | && |
| 1 | || |
判断语句
| 语句 | 描述 |
|---|---|
| if 语句 | if 语句 由一个布尔表达式后紧跟一个或多个语句组成。 |
| if...else 语句 | if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 |
| if 嵌套语句 | 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。 |
| switch 语句 | switch 语句用于基于不同条件执行不同动作。 |
| select 语句 | select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 |
以下描述了 select 语句的语法:
1每个case都必须是一个管道 2所有channel表达式都会被求值 3所有被发送的表达式都会被求值 4如果任意某个通信可以进行,它就执行;其他被忽略。 5如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 6否则: 7 1.如果有default子句,则执行该语句。 8 2.如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。 9
语法
Go语言的For循环有3中形式,只有其中的一种使用分号。
和 C 语言的 for 一样:
for init; condition; post { }
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
循环语句嵌套
1for [condition | ( init; condition; increment ) | Range] 2{ 3 for [condition | ( init; condition; increment ) | Range] 4 { 5 statement(s); 6 } 7 statement(s); 8}
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
for语句执行过程如下:
1①先对表达式1赋初值; 2②判别赋值表达式 init 是否满足给定条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
for 循环的 range 格式可以对 slice、map、数组、字符串、chan等进行迭代循环。格式如下:
1for key, value := range oldMap { 2 newMap[key] = value 3}
函数
Go 语言最少有个 main() 函数。 GO 语言每个文件最多只有一个init()函数,该函数会被自动调用用于初始化操作
你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。
函数声明告诉了编译器函数的名称,返回类型,和参数。
Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。
Go 语言函数定义格式如下:
1func function_name( [parameter list] ) [return_types] { 2函数体 3}
函数定义解析:
1func:函数由 func 开始声明 2function_name:函数名称,函数名和参数列表一起构成了函数签名。 3parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。 4return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。 5函数体:函数定义的代码集合。 6
初始化局部和全局变量
| 数据类型 | 初始化默认值 |
|---|---|
| int | 0 |
| float32 | 0 |
| pointer | nil |
声明数组
var variable_name [SIZE] variable_type
多维数组
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
声明指针
var var_name *var-type
定义指针数组
var ptr [MAX]*int
指向指针的指针
var ptr **int
定义结构体
variable_name := structure_variable_type {value1, value2...valuen}
结构体指针
var struct_pointer *Books
定义切片
var identifier []type
切片不需要说明长度。
或使用make()函数来创建切片:
1var slice1 []type = make([]type, len) 2也可以简写为 3slice1 := make([]type, len)
也可以指定容量,其中capacity为可选参数。
make([]T, length, capacity)
len() 方法获取长度
cap() 可以测量切片最长可以达到多少
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对的 key 值。
Map(集合)
可以使用内建函数 make 也可以使用 map 关键字来定义 Map和chan:
1/* 声明变量,默认 map 是 nil */ 2var map_variable map[key_data_type]value_data_type 3 4/* 使用 make 函数 */ 5map_variable := make(map[key_data_type]value_data_type) 6 7/* 使用make 函数 */ 8chan1 := make(chan type,(.capacity))
==如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对==
递归函数
1func recursion() { 2 recursion() /* 函数调用自身 */ 3} 4 5func main() { 6 recursion() 7} 8
1斐波那契数列 2package main 3 4import "fmt" 5 6var n1=0 7var n2=0 8 9func fibonacci(n int) int { 10 n1++ 11 if n < 2 { 12 return n 13 } 14 return fibonacci(n-2) + fibonacci(n-1) 15} 16 17func feibo(a,b,n int) int { 18 // 优化版,需要传起始参数,如果指定a,b大于0修改n>2 19 n2++ 20 if (n > 0) { 21 return feibo(a+b, a, n-1) 22 } 23 return a 24} 25 26func main() { 27 var i int 28 var j int 29 for i = 0; i < 10; i++ { 30 fmt.Printf("%d\t", fibonacci(i)) 31 } 32 fmt.Println(n1) 33 for j = 0; j < 10; j++ { 34 fmt.Printf("%d\t",feibo(0, 1, j)) 35 } 36 fmt.Println(n2) 37}
类型转换
type_name(expression)
==type_name 为类型,expression 为表达式。==
接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
1/* 定义接口 */ 2type interface_name interface { 3 method_name1 [return_type] 4 method_name2 [return_type] 5 method_name3 [return_type] 6 ... 7 method_namen [return_type] 8} 9 10/* 定义结构体 */ 11type struct_name struct { 12 /* variables */ 13} 14 15/* 实现接口方法 */ 16func (struct_name_variable struct_name) method_name1() [return_type] { 17 /* 方法实现 */ 18} 19... 20func (struct_name_variable struct_name) method_namen() [return_type] { 21 /* 方法实现*/ 22}
错误处理
error类型是一个接口类型,这是它的定义:
1type error interface { 2 Error() string 3}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
1func Sqrt(f float64) (float64, error) { 2 if f < 0 { 3 return 0, errors.New("math: square root of negative number") 4 } 5 // 实现 6}
go语言深拷贝
1import ( 2 "fmt" 3 "bytes" 4 "encoding/gob" 5) 6 7func main() { 8 a := make(map[int]int, 0) 9 b := map[int]int{1:1,2:2} 10 c := deepCopy(&a, &b) 11 fmt.Println(a,b,c) 12} 13 14func deepCopy(dst, src interface{}) error { 15 var buf bytes.Buffer 16 if err := gob.NewEncoder(&buf).Encode(src); err != nil { 17 return err 18 } 19 return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) 20} 21
时间格式
1const ( 2 ANSIC = "Mon Jan _2 15:04:05 2006" 3 UnixDate = "Mon Jan _2 15:04:05 MST 2006" 4 RubyDate = "Mon Jan 02 15:04:05 -0700 2006" 5 RFC822 = "02 Jan 06 15:04 MST" 6 RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone 7 RFC850 = "Monday, 02-Jan-06 15:04:05 MST" 8 RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" 9 RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone 10 RFC3339 = "2006-01-02T15:04:05Z07:00" 11 RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" 12 Kitchen = "3:04PM" 13 // Handy time stamps. 14 Stamp = "Jan _2 15:04:05" 15 StampMilli = "Jan _2 15:04:05.000" 16 StampMicro = "Jan _2 15:04:05.000000" 17 StampNano = "Jan _2 15:04:05.000000000" 18)
其它标记
| 占位符 | 说明 | 举例 | 输出 |
|---|
- | 总打印数值的正负号;对于%q(%+q)保证只输出ASCII编码的字符。| Printf("%+q", "中文") | "\u4e2d\u6587"
- | 在右侧而非左侧填充空格(左对齐该区域)| |
| 备用格式:为八进制添加前导 0(%#o)| Printf("%#U", '中') | U+4E2D<br>为十六进制添加前导 0x(%#x)或 0X(%#X),为 %p(%#p)去掉前导 0x;<br>如果可能的话,%q(%#q)会打印原始 (即反引号围绕的)字符串;<br>如果是可打印字符,%U(%#U)会写出该字符的<br>Unicode 编码形式(如字符 x 会被打印成 U+0078 'x')。
' ' | (空格)为数值中省略的正负号留出空白(% d);<br>以十六进制(% x, %X)打印字符串或切片时,在字节之间用空格隔开 | 0 | 填充前导的0而非空格;对于数字,这会将填充移到正负号之后
同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。 该语句声明的变量作用域仅在 if 之内。
1if v := math.Pow(x, n); v < lim { 2 return v 3 }
1// strings包 2// Author: Wjy 3import ( 4 "strings" 5 "fmt" 6) 7 8func main() { 9 s1 := "hello world" 10 //判断字符是否在字符串中 11 fmt.Println(strings.Contains(s1, "l")) 12 //判断字符串中是否有字符在s1中 13 fmt.Println(strings.ContainsAny(s1, "wh")) 14 fmt.Println(strings.ContainsRune(s1, 'h')) 15 //判断字符的次数 16 fmt.Println(strings.Count(s1, "l")) 17 18 s2 := "2018课堂笔记.txt" 19 if strings.HasPrefix(s2, "2018"){ 20 fmt.Println("前缀是2018") 21 } 22 if strings.HasSuffix(s2, "txt"){ 23 fmt.Println("后缀是txt") 24 } 25 //查找第一次出现的索引位置,不存在返回-1 26 fmt.Println(strings.Index(s1, "l")) 27 fmt.Println(strings.IndexAny(s1, "ado")) 28 查找最后一次出现的位置 29 fmt.Println(strings.LastIndex(s1, "l")) 30 31 a := []string{"1","2","3","4"} 32 b := strings.Join(a, "") 33 fmt.Println(b) 34 //字符串分割 35 fmt.Println(strings.Split(b,"")) 36 //字符串分割成几个,个数是-1==splite 37 fmt.Println(strings.SplitN(b, "", 3)) 38 //自己拼接自己 39 fmt.Println(strings.Repeat("hello", 2)) 40 //替换n为替换次数 41 fmt.Println(strings.Replace(b,"2","3",-1)) 42 //大写 43 fmt.Println(strings.ToUpper(s1)) 44 45 s6 := " **+hello+*world** " 46 //删除首尾字符 47 fmt.Println(strings.Trim(s6,"*+ ")) 48 //删除左侧 49 fmt.Println(strings.TrimLeft(s6, "")) 50 //删除右侧 51 fmt.Println(strings.TrimRight(s6, "")) 52 //去首位空格 53 fmt.Println(strings.TrimSpace(s6)) 54 55 s7:=s6[4:8] 56 fmt.Println(s7) 57 a:="我们去秋游" 58 fmt.Println(strings.Index(a, "去")) 59 fmt.Println(strings.Contains(a, "秋游")) 60}
1strconv转换 2func ParseBool(str string) (value bool, err error) 3//将字符串转换为布尔值 4strconv.ParseBool("1") // true 5 6func FormatBool(b bool) string 7//FormatBool 将布尔值转换为字符串 "true" 或 "false" 8strconv.FormatBool(0) 9 10func ParseFloat(s string, bitSize int) (f float64, err error) 11//将字符串转换为 float64 型,bitSize:指定浮点类型(32:float32、64:float64) 12 13func ParseInt(s string, base int, bitSize int) (i int64, err error) 14//将字符串转换为 int 类型,base:进位制(2 进制到 36 进制),bitSize:指定整数类型(0:int、8:int8、16:int16、32:int32、64:int64) 15 16func Atoi(s string) (i int, err error) 17//将字符串转换为 int 类型 18 19func FormatInt(i int64, base int) string 20//FormatUint 将 int 型整数 i 转换为字符串形式 21 22func Itoa(i int) string 23//返回数字 i 所表示的字符串类型的十进制数 24 25浮点数到字符串 26strconv.FormatFloat(float64(input_num), 'f', 6, 64) 27 28整形到字符串 29s = strconv.Itoa(i) 或者 s = FormatInt(int64(i), 10)
1math模块部分函数 2https://godoc.org/math 3fmt.Println(math.Abs(float64(i))) //绝对值 4 fmt.Println(math.Ceil(5.0)) //向上取整 5 fmt.Println(math.Floor(5.8)) //向下取整 6 fmt.Println(math.Mod(11, 3)) //取余数,同11%3 7 fmt.Println(math.Modf(5.26)) //取整数,取小数 8 fmt.Println(math.Pow(3, 2)) //x的y次方 9 fmt.Println(math.Pow10(4)) // 10的n次方 10 fmt.Println(math.Sqrt(8)) //开平方 11 fmt.Println(math.Cbrt(8)) //开立方
list操作 https://godoc.org/container/list#New
1func (e *Element) Next() *Element //返回该元素的下一个元素,如果没有下一个元素则返回nil 2func (e *Element) Prev() *Element//返回该元素的前一个元素,如果没有前一个元素则返回nil。 3type List 4func New() *List //返回一个初始化的list 5func (l *List) Back() *Element //获取list l的最后一个元素 6func (l *List) Front() *Element //获取list l的第一个元素 7func (l *List) Init() *List //list l初始化或者清除list l 8func (l *List) InsertAfter(v interface{}, mark *Element) *Element //在list l中元素mark之后插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。 9func (l *List) InsertBefore(v interface{}, mark *Element) *Element//在list l中元素mark之前插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。 10func (l *List) Len() int //获取list l的长度 11func (l *List) MoveAfter(e, mark *Element) //将元素e移动到元素mark之后,如果元素e或者mark不属于list l,或者e==mark,则list l不改变。 12func (l *List) MoveBefore(e, mark *Element)//将元素e移动到元素mark之前,如果元素e或者mark不属于list l,或者e==mark,则list l不改变。 13func (l *List) MoveToBack(e *Element)//将元素e移动到list l的末尾,如果e不属于list l,则list不改变。 14func (l *List) MoveToFront(e *Element)//将元素e移动到list l的首部,如果e不属于list l,则list不改变。 15func (l *List) PushBack(v interface{}) *Element//在list l的末尾插入值为v的元素,并返回该元素。 16func (l *List) PushBackList(other *List)//在list l的尾部插入另外一个list,其中l和other可以相等。 17func (l *List) PushFront(v interface{}) *Element//在list l的首部插入值为v的元素,并返回该元素。 18func (l *List) PushFrontList(other *List)//在list l的首部插入另外一个list,其中l和other可以相等。 19func (l *List) Remove(e *Element) interface{}//如果元素e属于list l,将其从list中删除,并返回元素e的值。
关于map嵌套赋值
1type t_classmates map[string]int 2 domitory := make(map[string]t_classmates) 3domitory["309"] = t_classmates{"wangwu": 25, "zhaoliu": 26,} 4或者 5a := make(map[string]map[string]int) 6c := make(map[string]int) 7a["key"]=c
底层一些的地方,bufio.Scanner,ioutil.ReadFile和ioutil.WriteFile使用的都是*os.File的 Read和Write方法
1switch语句写条件表达式case只能使用某个值或者变量,不能添加判断语句。 2switch省略条件表达式,可当 if...else if...else 使⽤ 3case关键字后加fallthrough关键字紧跟该条件的条件语句会顺序执行。
1#string到int 2int,err:=strconv.Atoi(string) 3#string到int64 4int64, err := strconv.ParseInt(string, 10, 64) 5#int到string 6string:=strconv.Itoa(int) 7#int64到string 8string:=strconv.FormatInt(int64,10) 9 10时间格式化yyyy-MM-dd hh:mm:ss 11var _startDate int64 = time.Now().Unix() 12var startDate string = time.Unix(_startDate, 0).Format("2006-01-02 15:04:05") 13 14int64转10位的时间 15t := time.Now().Unix() 16str := strconv.FormatInt(t, 10) 17 18数组赋值 var nums [...]int{2:1,3:2} 指定位置参数或者取下标赋值 19 20break 可⽤用于 for、switch、select,⽽而 continue 仅能⽤用于 for 循环。 21 22 23使⽤用 slice 对象做变参时,必须展开。 24func main() { 25 s := []int{1, 2, 3} 26 println(test("sum: %d", s...)) 27} 28func test2(s string,n ...int) string { 29 var x int 30 for _, i := range n{ 31 x += i 32 } 33 return fmt.Sprintf(s, x) 34}
1类 2package main 3 4import "fmt" 5 6// Author: Wjy 7type Worker struct { 8 name string 9 age int 10 sex string 11} 12 13type Cat struct { 14 color string 15 age int 16} 17 18func (w Worker) work() { 19 fmt.Println(w) 20} 21 22func (w Worker) eat() { 23 fmt.Println("工人吃") 24} 25 26func (c Cat) eat() { 27 fmt.Println(c) 28} 29 30func (c *Cat) sleep() { 31 fmt.Println(c.color,(*c).age) 32 //c.color = "黑" 33} 34 35func main() { 36 //w := Worker{"啊",30,"男"} 37 //w.work() 38 //w.eat() 39 // 40 //c := Cat{"红",1} 41 //c.eat() 42 //c.sleep() 43 //c1 := &c 44 //(*c1).eat() 45 46 f := 父类{"a",12} 47 f.eat() 48 z := 子类{父类{"b",3},"adsa"} 49 z.eat() 50} 51 52type 父类 struct { 53 name string 54 age int 55} 56 57type 子类 struct { 58 父类 59 school string 60} 61 62func (f 父类) eat() { 63 fmt.Println(f) 64} 65 66func (z 子类) eat() { 67 fmt.Println(z) 68}
1// 排序 2package main 3 4import ( 5 "sort" 6 "fmt" 7) 8 9// Author: Wjy 10type Person struct { 11 name string 12 age int 13} 14 15func (p *Person) String()string { 16 return fmt.Sprint(*p) 17} 18 19func main() { 20 p1:=Person{"aaa",30} 21 p2:=Person{"bbb",29} 22 p3:=Person{"ccc",31} 23 p4:=Person{"ddd",29} 24 p5:=Person{"eee",35} 25 26 s1:=make([]*Person,0) 27 s1 = append(s1,&p1,&p2,&p3,&p4,&p5) 28 29 s2 := PersionSlice(s1) 30 sort.Sort(s2) 31 fmt.Println(s2) 32} 33 34type PersionSlice []*Person 35 36func (slice PersionSlice) Less(i,j int) bool { 37 if slice[i].age < slice[j].age{ 38 return true 39 }else if slice[i].age > slice[j].age{ 40 return false 41 }else { 42 return slice[i].name<slice[j].name 43 } 44} 45 46func (slice PersionSlice) Swap(i,j int) { 47 slice[i],slice[j]=slice[j],slice[i] 48} 49 50func (slice PersionSlice) Len() int { 51 return len(slice) 52}
1// 接口 2package main 3 4import "fmt" 5 6// Author: Wjy 7type USB interface { 8 start() 9 end() 10} 11 12// 实现类 13type Mouse struct { 14 name string 15} 16 17func (m Mouse)start(){ 18 fmt.Println(m,"点点") 19} 20 21func (m Mouse)end(){ 22 fmt.Println(m,"ddd") 23} 24 25type Flash struct { 26 name string 27} 28 29func (f Flash)start(){fmt.Println("u",f)} 30func (f Flash)end(){fmt.Println(f)} 31 32func main() { 33 m := Mouse{"罗技"} 34 f := Flash{"闪迪"} 35 var usb USB // 定义接口类型的变量 36 // 接口对象不能访问实现类的属性 37 //usb = m 38 usb = f 39 usb.start() 40 41 testInterface(m) 42 43} 44 45func testInterface(usb USB){ 46 usb.start() 47}
1//断言 2package main 3 4import ( 5 "math" 6 "fmt" 7) 8// Author: Wjy 9type Shape interface { 10 peri() float64 11 area() float64 12} 13type Triangle struct { 14 a,b,c float64 15} 16func (t Triangle)peri()float64 { 17 return t.a+t.b+t.c 18} 19func (t Triangle)area()float64 { 20 p :=t.peri() / 2 21 s := math.Sqrt(p*(p-t.a)*(p-t.b)*(p-t.c)) 22 return s 23} 24func main() { 25 /* 26 多态 27 */ 28 t1 := Triangle{3,4,5} 29 fmt.Println(t1.peri()) 30 fmt.Println(t1.area()) 31 32 var s1 Shape 33 s1 = t1 34 fmt.Println(s1.peri()) 35 fmt.Println(s1.area()) 36 37 var c1 Circle 38 c1 = Circle{4} 39 fmt.Println(c1.peri()) 40 fmt.Println(c1.area()) 41 fmt.Println(c1.radius) 42 43 var s2 Shape = Circle{5} 44 fmt.Println(s2.area()) 45 fmt.Println(s2.peri()) 46 47 testShap(t1) 48 // 如果定义了一个接口类型数组可以传任意实现类对象 49 arr := [4]Shape{t1,s1,c1,s2} 50 fmt.Println(arr) 51 52 getType(s1) 53 getType(s2) 54 getType(c1) 55 56 getTpye2(s1) 57 getTpye2(t1) 58} 59 60type Circle struct { 61 radius float64 62} 63func (c Circle)peri()float64 { 64 return c.radius*2*math.Pi 65} 66func (c Circle)area()float64 { 67 return math.Pow(c.radius, 2)*math.Pi 68} 69//func (c Circle)new() { 70// fmt.Println(c,"new") 71//} 72func testShap(s Shape){ 73 fmt.Println(s.peri(),s.area()) 74} 75// 接口转实现类,方法1 76func getType(s Shape){ 77 //instance, ok := s.(Circle) // 判断实现类 78 //fmt.Println(instance.radius, ok) 79 80 if ins,ok:=s.(Triangle);ok{ 81 fmt.Println("三角形",ins.a,ins.b,ins.c) 82 }else if ins,ok:=s.(Circle);ok{ 83 fmt.Println("圆",ins.radius) 84 }else { 85 fmt.Println("不是圆和三角") 86 } 87} 88// 接口转实现类,方法2 89func getTpye2(s Shape){ 90 switch ins:=s.(type) { 91 case Triangle: fmt.Println("三角形",ins) 92 case Circle: fmt.Println("圆", ins.radius) 93 //case int: fmt.Println("整数") 94 } 95}
1// 自定义error error是内置类型跟nil类似 2package main 3 4import "fmt" 5 6// Author: Wjy 7 8func main() { 9 res1, ok := getArea(-4,6) 10 if ok != nil{ 11 fmt.Println(ok.Error()) 12 }else { 13 fmt.Println("面积是",res1) 14 } 15} 16 17type errorRect struct { 18 msg string 19 wid float64 20 len float64 21} 22 23func (e *errorRect) Error()string{ 24 return fmt.Sprintf("宽度是 %.2f,长度 %.2f, 错误信息 %s",e.wid,e.len,e.msg) 25} 26 27func getArea(wid, len float64)(float64,error){ 28 errorMsg := "" 29 if wid < 0{ 30 errorMsg = "宽度为复数" 31 } 32 if len < 0{ 33 if errorMsg == ""{ 34 errorMsg = "长度为复数" 35 }else { 36 errorMsg += ",长度为复数" 37 } 38 } 39 if errorMsg!=""{ 40 return 0,&errorRect{errorMsg,wid,len} 41 } 42 area := wid * len 43 return area,nil 44} 45
1//异常处理 2package main 3 4import "fmt" 5 6// Author: Wjy 7 8func main() { 9 //read:= bufio.NewReader(os.Stdin) 10 //data,_,_:=read.ReadLine() 11 //fmt.Println(string(data)) 12 13 funA() 14 funB() 15 fmt.Println("over") 16 17 funC() 18} 19 20func funA() { 21 fmt.Println("funcA()...") 22} 23func funB() { 24 defer func(){ 25 if msg:=recover();msg!=nil{ 26 fmt.Println(msg,"已恢复") 27 } 28 }() 29 fmt.Println("我是函数funB()...") 30 for i:=1;i<=10;i++{ 31 fmt.Println("i:",i) 32 if i == 5{ 33 // 让程序终端 34 panic("funB,恐慌")// 打断程序的执行 35 } 36 } 37} 38 39func funC() { 40 defer func() {fmt.Println("funC",recover())}() 41 fmt.Println("func") 42}
1// time包 2package main 3 4import ( 5 "time" 6 "fmt" 7 "math/rand" 8) 9 10// Author: Wjy 11func main() { 12 t1:=time.Now() // 获取当前时间 13 fmt.Printf("%T\n",t1) 14 fmt.Println(t1) 15 // 指定日期时间 16 t2 := time.Date(2018,10,22,16,30,28,0,time.Local) 17 fmt.Println(t2) 18 // 时间对象转字符串 19 fmt.Println(t1.String()) 20 // 格式化输出日期 21 fmt.Println(t1.Format("Mon Jan 2 15:04:05 -0700 MST 2006")) 22 // 6-1-2-3-4-5 23 fmt.Println(t1.Format("2006年01月02日 15:04:05")) 24 // 字符串转时间对象 25 s3 := "1999年10月10日" 26 ss3,ok:=time.Parse("2006年01月02日",s3) 27 fmt.Println(ss3,ok) 28 29 t4 := time.Date(1970,1,1,1,0,0,0,time.UTC) 30 fmt.Println(t4.Unix()) // 获取秒数 31 fmt.Println(t1.Unix()-t4.Unix()) // 格林威治时间到现在的秒数 32 33 fmt.Println(t1.UnixNano()-t4.UnixNano()) // 格林威治时间到现在的微秒 34 35 y,m,d := t1.Date() // 获取当前时间对象的年,月,日 36 fmt.Println(y,m,d) 37 h,min,s := t1.Clock() // 获取当前时间对象的时,分,秒 38 fmt.Println(h,min,s) 39 40 year2 := t1.Year() 41 fmt.Println(year2) 42 fmt.Println(t1.YearDay()) 43 month2 := t1.Month() 44 fmt.Println(month2) 45 fmt.Println(t1.Day()) 46 fmt.Println(t1.Hour()) 47 fmt.Println(t1.Minute()) 48 fmt.Println(t1.Second()) 49 fmt.Println(t1.Weekday()) 50 fmt.Println(t1.ISOWeek()) // 年,周 51 52 t5 := t1.Add(time.Minute) 53 fmt.Println(t1,t5) 54 fmt.Println(t1.Add(24*time.Hour)) 55 56 fmt.Println(t5.Sub(t1)) // 求两时间差 57 //time.Sleep(time.Second*10) 58 59 rand.Seed(time.Now().UnixNano()) 60 randNum := rand.Intn(10)+1 61 time.Sleep(time.Duration(randNum)*time.Second) 62 fmt.Println(time.Duration(randNum)*time.Second) 63}
1//文件操作 2package main 3 4import ( 5 "os" 6 "fmt" 7 "path" 8 "path/filepath" 9) 10 11// Author: Wjy 12func main() { 13 file,ok := os.Stat("./src") 14 if ok != nil{ 15 fmt.Println(ok) 16 return 17 } 18 //os.Open("") 19 fmt.Printf("%T\n",file) 20 fmt.Println(file.Name()) 21 fmt.Println(file.IsDir()) 22 fmt.Println(file.Mode()) 23 fmt.Println(file.ModTime()) 24 fmt.Println(file.Size()) 25 26 filename := "E:/goland/bin" 27 filename1 := "/home/path" 28 fmt.Println(path.IsAbs(filename1)) 29 fmt.Println(filepath.IsAbs(filename)) 30 31 fmt.Println(filepath.Abs(filename)) 32 33 fmt.Println(path.Join(filename,"..")) 34 // 创建文件夹 35 //errs:=os.MkdirAll("./newpath/w/d/s",777) 36 //if errs !=nil{ 37 // fmt.Println(errs) 38 //}else { 39 // fmt.Println("ok") 40 //} 41 // 如果文件存在create会覆盖创建 42 //tfile,err := os.Create("测试用的文件.txt") 43 //fmt.Println(err) 44 //tfile.WriteString("lakakkakakasduhiuh我i我i我i我i") 45 //tfile.Close() 46 47 //fil,err :=os.Open("测试用的文件.txt") 48 //fmt.Println(err) 49 //fmt.Println(fil) 50 //fil.Close() 51 52 fil1,err1:=os.OpenFile("测试用的文件.txt",os.O_RDWR|os.O_CREATE,777) 53 fmt.Println(fil1,err1) 54 fil1.Close() 55 56 // 可以删除文件和空目录 57 //err :=os.Remove("./测试用的文件.txt") 58 //fmt.Println(err) 59 //err := os.RemoveAll("目录")//删除整个目录 60}
1// 文件读写 2package main 3 4import ( 5 "os" 6 "fmt" 7 "io" 8) 9 10// Author: Wjy 11func main() { 12 /* 13 打开文件 14 读取文件 15 file.Read([]byte)-->n,err 16 n: 读取的数据个数 17 err: 错误信息 18 关闭文件 19 */ 20 filename := "./测试用的文件.txt" 21 file,err:=os.Open(filename) 22 if err!=nil{ 23 fmt.Println(err) 24 return 25 } 26 bs := make([]byte,5,5) 27 for { 28 n,err :=file.Read(bs) 29 if n == 0 || err == io.EOF{ 30 fmt.Println("结束了") 31 break 32 } 33 fmt.Println(n) 34 fmt.Println(bs[:n]) 35 fmt.Println(string(bs[:n])) 36 } 37 //n,err=file.Read(bs) 38 //fmt.Println(n) 39 //fmt.Println(bs) 40 //fmt.Println(string(bs)) 41 //fmt.Println(err) 42 // 43 //n,err=file.Read(bs) 44 //if n < 5{ 45 // fmt.Println(n) 46 // fmt.Println(bs) 47 // fmt.Println(string(bs[:n])) 48 // fmt.Println(err) 49 // return 50 //} 51 52 file.Close() 53 54}
1// ioutil包读写文件 2package main 3 4import ( 5 "os" 6 "bufio" 7 "strings" 8) 9 10// Author: Wjy 11func main() { 12 //filename1 := "./ddd.txt" 13 //data, err:= ioutil.ReadFile(filename1) 14 //fmt.Println(string(data),err) 15 // 16 //filename2 := "./ddd.py" 17 //s1 := "print 'hello world'" 18 //err =ioutil.WriteFile(filename2,[]byte(s1),os.ModePerm) 19 //fmt.Println(err) 20 21 // 读取字符串 22 //a := "zxcuoioiasn2l3j" 23 //r := strings.NewReader(a) 24 //data,err :=ioutil.ReadAll(r) // 返回byte切片 25 //fmt.Println(data,err) 26 27 // 读取目录 28 //l, _:=ioutil.ReadDir("./src/") 29 //for _,v := range l{ 30 // fmt.Println(v.Name()) 31 // if v.IsDir() == true{ 32 // l1,_ := ioutil.ReadDir("./src/"+v.Name()) 33 // for _, j:=range l1{ 34 // fmt.Println(j.Name(), j.IsDir()) 35 // } 36 // } 37 //} 38 39 // 缓存读写bufio 40 //filename := "ddd.txt" 41 //file,_:=os.Open(filename) 42 //r1 := bufio.NewReader(file) 43 //data,_,_:=r1.ReadLine() 44 //fmt.Println(string(data)) 45 //data,_,_=r1.ReadLine() 46 //fmt.Println(string(data)) 47 //data,_,_=r1.ReadLine() 48 //fmt.Println(string(data)) 49 50 //for { 51 // s1, err := r1.ReadString(10) 52 // if err == io.EOF{ 53 // break 54 // } 55 // fmt.Print(s1) 56 //} 57 //file.Close() 58 59 //for { 60 //data,err:=r1.ReadBytes('\n') 61 //if err == io.EOF{break} 62 //fmt.Print(string(data)) 63 //} 64 //file.Close() 65 66 //r2:=bufio.NewReader(os.Stdin) 67 //s2,_:=r2.ReadString('\n') 68 //fmt.Println(s2) 69 70 // 计算两日期差 71 //t1 :=time.Date(2018,2,10,13,59,0,0,time.Local) 72 //t2 :=time.Now() 73 //subM:=t2.Sub(t1) 74 //time_hour :=int(subM.Hours()) 75 //fmt.Println(time_hour/24) 76 // 77 //fmt.Println(t2.Format("2006 15:04:05")) 78 //fmt.Println(int(t2.Unix())) 79 80 //file4,_ := os.OpenFile("标准输出.txt",os.O_RDWR|os.O_CREATE,os.ModePerm) 81 //r3:=bufio.NewReader(os.Stdin) 82 //s3,_:=r3.ReadString('\n') 83 //r4 := bufio.NewWriter(file4) 84 //s4,err := r4.WriteString(s3) 85 //r4.Flush() 86 //file4.Close() 87 //fmt.Println(s4,err) 88 89 file5,_:=os.OpenFile("标准输出1.txt",os.O_RDWR|os.O_CREATE,os.ModePerm) 90 defer file5.Close() 91 r3 := bufio.NewReader(strings.NewReader("hello world")) 92 //s3,_:=r3.ReadString('\n') 93 r4 := bufio.NewWriter(file5) 94 r4.ReadFrom(r3) 95 r4.Flush() 96}
1// math包 2var num int 3 fmt.Scanln(&num) 4 fmt.Printf("%T %v",num, error) 5 num := 2.1 6 fmt.Println(num/int(math.Pow10(3)), num%int(math.Pow10(3))/int(math.Pow10(2))) 7 fmt.Println(math.Floor(num+0.5)) // 向下取整 8 fmt.Println(math.Ceil(num)) // 向上取整 9 fmt.Println(math.Max(1,2)) //最大值 10 fmt.Println(math.Min(1,2)) // 最小值 11 fmt.Println(math.Pow(2,3)) //幂数 12 fmt.Println(math.Mod(11,3)) // 求余数 13 fmt.Println(math.Modf(3.18)) // 整数和精度 14 fmt.Println(math.Sqrt(9)) // 开平方
1// defer坑 2// Author: Wjy 3 4package main 5 6import "fmt" 7 8// defer是栈的数据结构 9 10func main() { 11 //a:=1 12 //fmt.Println(f()) 13 //fmt.Println(f1()) 14 //fmt.Println(f2()) 15 16 //fmt.Println(f3()) 17 //fmt.Println(f4()) 18 //fmt.Println(f5()) 19 //var pil *int 20 //a := 10 21 //pil = &a 22 //fmt.Println(&a,&pil) 23 //f6(pil) 24 25 //var pill **int 26 //pill = &pil 27 //fmt.Println(**pill) 28 29 //map1:=make(map[int]int) 30 //fmt.Printf("%p\n",map1) 31 //fmt.Println(&map1) 32 33 //arr := [2]int{1,2} 34 //arr1 := &arr 35 //arr1[0] = 100 36 //fmt.Println(arr1) 37 38 a:=[...]int{1,2,3} 39 f7(&a) 40 41 b := 1 42 c := 2 43 d := [2]*int{&b,&c} 44 f8(d) 45 fmt.Println(b,c) 46} 47 48func f()(result int){ 49 defer func() {result++}() 50 return 0 51} 52 53func f1()(r int){ 54 t := 5 55 defer func() {t = t+5}() 56 return t 57} 58 59func f2()(r int){ 60 defer func(r int) {r=r+5}(r) 61 return 1 62} 63 64func f3() (result int) { 65 result = 0 66 func() {result++}() 67 return 68} 69 70func f4() (r int) { 71 t:=5 72 r=t 73 func() {t=t+5}() 74 return 75} 76 77func f5() (r int) { 78 r = 1 79 func(r int){ 80 r = r +5 81 }(r) 82 return 83} 84 85func f6(a *int) { 86 fmt.Println(*a) 87} 88 89func f7(a *[3]int) { 90 fmt.Println(*a) 91} 92 93func f8(a [2]*int) { 94 fmt.Println(a) 95 *a[0] = 111 96 fmt.Println(a) 97}
计时器
1// timer 2package main 3 4import ( 5 "time" 6 "fmt" 7) 8 9// Author: Wjy 10func main() { 11 /* 12 计时器 13 */ 14 timer1:=time.NewTimer(3*time.Second) 15 fmt.Printf("%T\n", timer1) 16 fmt.Println(time.Now()) 17 time1 := <- timer1.C // 定时器 返回 <- chan Time 18 fmt.Println(time1) 19 timer1.Reset(2*time.Second) 20 time2 := <- timer1.C 21 fmt.Println(time2) 22 time3 := time.After(3*time.Second) 23 fmt.Println(time3) 24 timer1.stop() // 停止计时器 25 ok := timer1.restart(1*time.Second) // 重置计时器 26 fmt.Println(ok) 27 // 定时器, 定时任务 28 tk := time.NewTicker(1 * time.Second) 29 i := 0 30 for { 31 <-tk.C 32 fmt.Println(1) 33 i++ 34 if i ==5{ 35 break 36 } 37 } 38 }
生成随机时间对象
1rand.Seed(time.Now().UnixNano()) 2a:=time.Duration(rand.Intn(1000)) 3fmt.Println(a)
同步等待组
1package main 2 3import ( 4 "sync" 5 "fmt" 6 ) 7var count int = 1 8// Author: Wjy 9func main() { 10 var s sync.WaitGroup 11 s.Add(4) // 协程并发数量 12 go 卖票("窗口1",&s) 13 go 卖票("窗口2",&s) 14 go 卖票("窗口3",&s) 15 go 卖票("窗口4",&s) 16 s.Wait() // 等待并发数量为0停止主程序 17} 18 19func 卖票(str string,s *sync.WaitGroup){ 20 for count <= 100{ 21 fmt.Println(str,count) 22 count++ 23 } 24 s.Done() 25}
同步等待加锁
1package main 2 3import ( 4 "sync" 5 "fmt" 6 "math/rand" 7 "time" 8) 9var count int = 100 10var Mutex sync.Mutex 11var s sync.WaitGroup 12// Author: Wjy 13func main() { 14 s.Add(4) 15 go 卖票("窗口1") 16 go 卖票("窗口2") 17 go 卖票("窗口3") 18 go 卖票("窗口4") 19 s.Wait() 20} 21 22func 卖票(str string){ 23 rand.Seed(time.Now().UnixNano()) 24 for { 25 Mutex.Lock() 26 if count > 0{ 27 time.Sleep(time.Duration(rand.Intn(1000))) 28 fmt.Println(str,count) 29 count-- 30 }else { 31 fmt.Println("卖完了") 32 Mutex.Unlock() 33 break 34 } 35 Mutex.Unlock() 36 } 37 s.Done() 38 39}
互斥锁
1package main 2 3import ( 4 "sync" 5 "fmt" 6 "time" 7) 8 9// Author: Wjy 10func main() { 11 var mutes sync.Mutex 12 13 fmt.Println("锁定") 14 mutes.Lock() 15 fmt.Println("已经锁定") 16 for i:=1;i<=3;i++{ 17 go func(i int) { 18 fmt.Println(i,"即将") 19 mutes.Lock() 20 fmt.Println(i,"已经") 21 }(i) 22 } 23 time.Sleep(5*time.Second) 24 fmt.Println("解锁") 25 mutes.Unlock() 26 fmt.Println("已经解锁") 27 time.Sleep(3*time.Second) 28}
读写锁
1// 读写锁读锁可以并发,写锁只能等待解锁 2package main 3 4import ( 5 "sync" 6 "fmt" 7 "math/rand" 8 "time" 9) 10 11// Author: Wjy 12 13 14var n int = 100 15var wg sync.WaitGroup 16var rwm sync.RWMutex 17func main() { 18 // 创建10个协程 19 wg.Add(10) 20 21 for i:=1;i<=5;i++{ 22 go read(i) 23 } 24 for i:=1;i<=5;i++{ 25 go write(i) 26 } 27 wg.Wait() 28} 29 30func write(i int) { 31 defer wg.Done() 32 rand.Seed(time.Now().UnixNano()) 33 rwm.Lock() 34 fmt.Println("写操作",i) 35 randnum := rand.Intn(100)+1 36 n = randnum 37 fmt.Println(i,"写入",randnum) 38 rwm.Unlock() 39} 40 41func read(i int) { 42 defer wg.Done() 43 rwm.RLock() 44 fmt.Println("读操作",i) 45 v := n 46 fmt.Println(i,"读取了",v) 47 rwm.RUnlock() 48}
Cond实现了一个条件变量
1package main 2 3import ( 4 "fmt" 5 "sync" 6 "time" 7) 8 9func main() { 10 var count int = 4 11 var wg sync.WaitGroup 12 wg.Add(5) 13 14 // 新建 cond 15 var mutex sync.Mutex 16 cond := sync.NewCond(&mutex) 17 18 for i := 0; i < 5; i++ {//0,1,2,3,4 19 go func(i int) {//g1,g2,g3,g4,g5 20 //0, 1, 2, 3, 4 21 // 争抢互斥锁的锁定 22 cond.L.Lock() //g1 23 24 // 条件是否达成count:1 25 for count > i {//0,1,2 26 cond.Wait()//g0,g1,g2 27 fmt.Printf("收到一个通知 goroutine%d\n", i) 28 } 29 30 fmt.Printf("goroutine%d 执行结束\n", i) 31 32 cond.L.Unlock() 33 wg.Done() 34 }(i) 35 } 36 37 // 确保所有 goroutine 启动完成 38 time.Sleep(time.Millisecond * 20) 39 // 锁定一下,我要改变 count 的值 40 fmt.Println("broadcast...") 41 cond.L.Lock() 42 count -= 1 // 3 43 cond.Broadcast() 44 fmt.Println("第一次:广播结束。。") 45 cond.L.Unlock() 46 47 time.Sleep(2*time.Second) 48 fmt.Println("-------------------------") 49 fmt.Println("signal...") 50 cond.L.Lock() 51 count -= 2 // 1 52 cond.Signal() 53 fmt.Println("第二次:单发通知结束。。") 54 cond.L.Unlock() 55 56 57 time.Sleep(2*time.Second) 58 fmt.Println("------------------------") 59 fmt.Println("broadcast...") 60 cond.L.Lock() 61 count -= 1 // 0 62 cond.Broadcast() 63 fmt.Println("第三次:广播结束。。") 64 cond.L.Unlock() 65 66 wg.Wait() 67}
内置原子性数值操作和其他操作
1package main 2 3import ( 4 "fmt" 5 "sync/atomic" 6 "sync" 7 "runtime" 8) 9 10// Author: Wjy 11func main() { 12 // 针对数值 13 var n int64 = 3 14 fmt.Println("n的输出") 15 newn := atomic.AddInt64(&n, 1) 16 fmt.Println(newn) 17 // 替换 18 atomic.SwapInt64(&n, 9) 19 fmt.Println(n) 20 // 比较交换 原值,是否是某个值,如果是交换 21 atomic.CompareAndSwapInt64(&n, 9,10) 22 fmt.Println(n) 23 // 一次性操作, 只会执行一次 24 var count int64= 0 25 once :=sync.Once{} 26 for i:=1;i<=10;i++{ 27 once.Do(func() { 28 count++ 29 }) 30 } 31 fmt.Println(count) 32 // 临时对象池, 如果获取不到数据就从给定的函数返回值获取 33 fun:= func() interface{}{ 34 return atomic.AddInt64(&count,1) 35 } 36 pool := sync.Pool{New:fun} 37 // 获取数据 38 fmt.Println(pool.Get()) 39 pool.Put(10) 40 pool.Put(8) 41 pool.Put(2) 42 pool.Put(3) 43 fmt.Println(pool.Get()) 44 //fmt.Println(pool.Get()) 45 //fmt.Println(pool.Get()) 46 //fmt.Println(pool.Get()) 47 48 // 执行GC() 49 runtime.GC() 50 pool.New = nil 51 fmt.Println(pool.Get()) 52}
正则
1package main 2 3import ( 4 "regexp" 5 "fmt" 6) 7 8// Author: Wjy 9func main() { 10 //a := "我们是程序员,我们不一样" 11 //r :=regexp.MustCompile(`我.{2}`) // 规则 12 //list :=r.FindAllStringSubmatch(a,-1) // 查找所有符合的返回[[我们是] [我们不]] 13 //fmt.Println(list[0][0]) 14 15 //b := "3.14 567 agsa 1.23 7. 8.99 lasdg 6.66" 16 //r1 ,err:= regexp.Compile(`\d+\.\d+`) 17 //if err != nil{ 18 // fmt.Println("缺少规则") 19 //} 20 //str := r1.FindAllString(b,-1) 21 //fmt.Println(str) 22 23 c := ` 24 <!DOCTYPE html> 25 <html lang="zh-CN"> 26 <head> 27 <title>Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国</title> 28 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"> 29 <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1"> 30 <meta charset="utf-8"> 31 <link rel="shortcut icon" href="/static/img/go.ico"> 32 <link rel="apple-touch-icon" type="image/png" href="/static/img/logo2.png"> 33 <meta name="author" content="polaris <polaris@studygolang.com>"> 34 <meta name="keywords" content="中文, 文档, 标准库, Go语言,Golang,Go社区,Go中文社区,Golang中文社区,Go语言社区,Go语言学习,学习Go语言,Go语言学习园地,Golang 中国,Golang中国,Golang China, Go语言论坛, Go语言中文网"> 35 <meta name="description" content="Go语言文档中文版,Go语言中文网,中国 Golang 社区,Go语言学习园地,致力于构建完善的 Golang 中文社区,Go语言爱好者的学习家园。分享 Go 语言知识,交流使用经验"> 36 </head> 37 <div>啊啊啊</div> 38 <div>不不不</div> 39 <div>产产产</div> 40 <div>顶顶顶 41asd 42asd</div> 43 <frameset cols="15,85"> 44 <frame src="/static/pkgdoc/i.html"> 45 <frame name="main" src="/static/pkgdoc/main.html" tppabs="main.html" > 46 <noframes> 47 </noframes> 48 </frameset> 49 </html>` 50 re,err := regexp.Compile(`<div>(?s:(.*?))</div>`) 51 if err != nil{ 52 fmt.Println(err) 53 return 54 } 55 // 取分组值 56 fmt.Println(re.FindAllStringSubmatch(c,-1)[1][1]) 57}
结构体生成json
1package main 2 3import ( 4 "fmt" 5 "encoding/json" 6) 7 8// Author: Wjy 9func main() { 10 // 结构体转json 11 s := IT{"itcase", []string{"Go","C++","Python","Test"},true, 666.666} 12 //js,err := json.Marshal(s) 13 js, err := json.MarshalIndent(s, "", " ") // 格式化 14 if err != nil{ 15 fmt.Println(err) 16 return 17 } 18 fmt.Println(string(js)) 19} 20 21type IT struct { 22 Company string `json:"company"` // 二次编码成小写 23 Subjects []string `json:"-"` // 此字段不会输出 24 IsOk bool `json:",string"` // 以字符串形式显示 25 Price float64 26}
map转json
1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6 "strconv" 7) 8 9// Author: Wjy 10 11func main() { 12 m := make(map[string]interface{}, 4) 13 m["company"] = "itcast" 14 m["subjects"] = []string{"Go","C++","Python","Test"} 15 m["isok"] = strconv.FormatBool(true) 16 m["price"] = strconv.FormatFloat(666.666,'f',3,64) 17 18 result,err := json.Marshal(m) 19 if err != nil{ 20 fmt.Println(err) 21 return 22 } 23 fmt.Println(string(result)) 24}
json转结构体
1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6) 7 8// Author: Wjy 9func main() { 10 json1 := `{"company":"itcast","isok":"true","price":"666.666","subjects":["Go","C++","Python","Test"]}` 11 var tmp IT 12 err := json.Unmarshal([]byte(json1), &tmp) 13 if err != nil{ 14 fmt.Println(err) 15 return 16 } 17 fmt.Printf("%T\n",tmp.IsOk) 18 // 如果只定义json数据部分字段结构体则只会取到定义部分数据 19 type IT2 struct { 20 Company string `json:"company"` 21 } 22 var tmp2 IT2 23 err = json.Unmarshal([]byte(json1), &tmp2) 24 if err != nil{ 25 fmt.Println(err) 26 return 27 } 28 fmt.Println(tmp2) 29} 30 31type IT struct { 32 Company string `json:"company"` // 二次编码成小写 33 Subjects []string `json:"subjects"` 34 IsOk bool `json:"isok,string"` 35 Price float64 `json:"price,string"` 36}
json转map
1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6) 7 8// Author: Wjy 9func main() { 10 map1 := make(map[string]interface{}) 11 json1 := `{"company":"itcast","isok":true,"price":666.666,"subjects":["Go","C++","Python","Test"]}` 12 13 err := json.Unmarshal([]byte(json1), &map1) 14 if err != nil{ 15 fmt.Println(err) 16 return 17 } 18 //fmt.Println(map1) 19 20 for k, v := range map1{ 21 switch data:=v.(type) { 22 case bool:map1[k] = data 23 map1[k] = data 24 case string: 25 case []interface{}:map1[k] = data 26 case float64:map1[k]= data 27 } 28 } 29 fmt.Printf("%T",map1["subjects"]) 30}
runtime包
1package main 2 3import ( 4 "fmt" 5 "runtime" 6) 7 8// Author: Wjy 9func main() { 10 fmt.Println(runtime.NumCPU()) //逻辑cpu核数 11 runtime.GOMAXPROCS(4) // 最大并发量,cpu核心数 12 go func() { 13 for i:=0;i<5;i++{ 14 runtime.Goexit() // 退出协程 15 fmt.Println("go") 16 } 17 }() 18 19 for i:=0;i<2;i++{ 20 runtime.Gosched() // 让出时间片 21 fmt.Println("hello") 22 } 23}
tcp客户端
1package main 2 3import ( 4 "net" 5 "fmt" 6 "bufio" 7 "os" 8) 9 10// Author: Wjy 11func main() { 12 conn, err := net.Dial("tcp",":5000") 13 if err != nil{ 14 fmt.Println(err) 15 return 16 } 17 defer conn.Close() 18 buf := make([]byte, 1024) 19 fmt.Println("输入内容") 20 for { 21 read := bufio.NewReader(os.Stdin) 22 data, _,_ := read.ReadLine() 23 conn.Write([]byte(data)) 24 n, err := conn.Read(buf) 25 if err!= nil{ 26 fmt.Println(err) 27 break 28 } 29 if string(buf[:n]) == "byby"{ 30 break 31 } 32 fmt.Println(string(buf[:n])) 33 } 34 35 36 37}
tcp服务端
1package main 2import ( 3 "net" 4 "fmt" 5 "strings" 6) 7// Author: Wjy 8func main() { 9 conn, err := net.Listen("tcp", ":5000") 10 if err != nil{ 11 fmt.Println(err) 12 return 13 } 14 defer conn.Close() 15 fmt.Println("服务已启动") 16 for { 17 data, err := conn.Accept() 18 if err != nil{ 19 fmt.Println(err) 20 return 21 } 22 go HandleConn(data) 23 } 24} 25 26func HandleConn(data net.Conn) { 27 addr := data.RemoteAddr().String() 28 defer data.Close() 29 buf := make([]byte, 2048) 30 for { 31 n, err := data.Read(buf) 32 if err != nil{ 33 fmt.Println(err) 34 return 35 } 36 if string(buf[:n]) == "quite"{ 37 data.Write([]byte("byby")) 38 return 39 } 40 fmt.Println(addr, ":", string(buf[:n])) 41 data.Write([]byte(strings.ToUpper(string(buf[:n])))) 42 } 43}
tcp服务端
1package main 2import ( 3 "net" 4 "fmt" 5 "time" 6) 7// Author: Wjy 8func main() { 9 client,err := net.Listen("tcp",":8001") // 服务端监听端口 10 if err != nil{ 11 fmt.Println("服务器异常:", err) 12 return 13 } 14 fmt.Println("服务已启动") 15 for { 16 conn, err := client.Accept() 17 fmt.Println("客户已连接:",conn.RemoteAddr().String()) 18 if err != nil{ 19 fmt.Println("连接异常:",err) 20 continue 21 } 22 go 处理(conn) 23 } 24} 25func 处理(conn net.Conn) { 26 buf := make([]byte, 2048) 27 for { 28 n, err := conn.Read(buf) 29 if err != nil{ 30 fmt.Println("客户离开:",err) 31 return 32 } 33 go 发送(string(buf[:n]), conn) 34 } 35 defer conn.Close() 36} 37 38func 发送(s string, conn net.Conn) { 39 conn.Write([]byte(time.Now().String() +":"+ s)) 40}
tcp客户端
1package main 2 3import ( 4 "net" 5 "fmt" 6 "bufio" 7 "os" 8 "io" 9) 10// Author: Wjy 11func main() { 12 conn, err := net.Dial("tcp",":8001") // 客户端接口 13 if err != nil{ 14 fmt.Println(err) 15 return 16 } 17 defer conn.Close() 18 19 buf := make([]byte, 2048) 20 go func() { 21 for { 22 n, err := conn.Read(buf) 23 if n == 0 || err == io.EOF{ 24 fmt.Println(err) 25 return 26 } 27 fmt.Println(string(buf[:n])) 28 } 29 }() 30 31 for { 32 reader := bufio.NewReader(os.Stdin) 33 data, err:= reader.ReadString('\n') 34 if err != nil{ 35 fmt.Println("read:", err) 36 return 37 } 38 if data[:len(data)-1] == "quite"{ 39 break 40 } 41 conn.Write([]byte(data[:len(data)-1])) 42 } 43}
聊天服务器需要nc工具
1package main 2import ( 3 "fmt" 4 "net" 5 "strings" 6 "time" 7 "syscall" 8) 9 10// 用户结构体 11type Client struct { 12 C chan string //用户发送数据的管道 13 Name string // 用户名 14 Addr string // 网络地址 15} 16 17// 保存在线用户 18var onlineMap map[string]Client 19 20// 通讯的管道 21var message = make(chan string) 22 23func main() { 24 //1. 监听 25 listener, err := net.Listen("tcp", "127.0.0.1:5000") 26 27 if err != nil { 28 fmt.Println("net.Listen:", err) 29 return 30 } 31 defer listener.Close() 32 33 // 新开一个协成,转发消息,只要有消息来了就遍历map发消息 34 go Manager() 35 36 //2. 主协成,阻塞等待用户 37 for { 38 conn, err := listener.Accept() 39 if err != nil { 40 fmt.Println("listener.Accept:", err) 41 continue //记住 42 } 43 // 处理用户连接 44 go HandleConn(conn) 45 } 46} 47//处理用户连接 48func HandleConn(conn net.Conn) { 49 defer conn.Close() 50 // 获取用户地址 51 cliAddr := conn.RemoteAddr().String() 52 // 创建一个结构体 53 cli := Client{make(chan string), cliAddr, cliAddr} 54 55 // 结构体添加到map 56 onlineMap[cliAddr] = cli 57 58 // 新开一个协成,专门给当前客户端发送信息 59 go WriteMsgToClient(cli, conn) 60 61 // 广播某个用户在线 62 message <- MakeStr(cli, "login") 63 64 // 提示我是谁 65 cli.C <- MakeStr(cli, "I here") 66 67 //定义一个管道判断是否主动退出 68 isQuit := make(chan bool) 69 //定义一个通道判断是否超时 70 isTimeOut := make(chan bool) 71 72 // 新开一个协成,接受用户发送过来的数据 73 go func() { 74 buf := make([]byte, 1024*2) 75 for { 76 n, err := conn.Read(buf) 77 if n == 0 { // 对方断开 78 isQuit <- true 79 fmt.Println("conn.Read:", err) 80 return 81 } 82 // 获取用户信息 83 userMsg := string(buf[:n-1]) // windows nc测试多个换行 84 85 // 处理字符 86 if len(userMsg) == 3 && userMsg == "who" { 87 //遍历map给当前用户发送所有成员 88 conn.Write([]byte("user list:\n")) 89 for _, tmp := range onlineMap { 90 userMsg = tmp.Addr + ":" + tmp.Name + "\n" 91 conn.Write([]byte(userMsg)) 92 } 93 } else if len(userMsg) >= 8 && userMsg[:6] == "rename" { 94 // rename|yoyo 95 name := strings.Split(userMsg, "|")[1] //窃取后面一个 96 //重新复制 97 cli.Name = name 98 onlineMap[cliAddr] = cli 99 100 //广播给自己 101 cli.C <- MakeStr(cli, "rename successfully") 102 103 } else { 104 // 转发此内容 105 message <- MakeStr(cli, userMsg) 106 } 107 108 // 进来这里就有数据不超时 109 isTimeOut <- true 110 } 111 }() 112 for { 113 // 通过select检车channel流动 114 select { 115 case <-isQuit: 116 delete(onlineMap, cliAddr) //移除当前用户 117 message <- MakeStr(cli, "logout") //广播当前用户下线 118 119 return 120 case <-isTimeOut: 121 122 case <-time.After(10 * time.Second): 123 delete(onlineMap, cliAddr) 124 message <- MakeStr(cli, "timeout") 125 return 126 } 127 } 128 129} 130func Manager() { 131 // 给map分配空间 132 onlineMap = make(map[string]Client) 133 134 for { 135 msg := <-message // 没有消息就阻塞 136 137 // 遍历map给每个成员发消息 138 for _, cli := range onlineMap { 139 cli.C <- msg // 消息给管道! 140 } 141 } 142} 143// 给当前发送消息 144func WriteMsgToClient(cli Client, conn net.Conn) { 145 // 给当前客服端发送信息 146 for msg := range cli.C { 147 conn.Write([]byte(msg + "\n")) 148 } 149} 150// 制作格式字符串 151func MakeStr(cli Client, str string) string { 152 return "[" + cli.Addr + "]" + cli.Name + ":" + str 153}
http包
1package main 2 3import ( 4 "net/http" 5 "fmt" 6) 7 8// Author: Wjy 9func main() { 10 // 注册处理函数,用户连接,自动调用指定处理函数 11 http.HandleFunc("/", HandConn) 12 // 监听绑定 13 http.ListenAndServe("0.0.0.0:8002", nil) 14} 15 16func HandConn(w http.ResponseWriter, req *http.Request) { 17 // w 给客户端回复数据, req 读取客户端发送的数据 18 fmt.Println(req.Method) // 请求方法 19 fmt.Println(req.Header) // 请求头 20 fmt.Println(req.URL) // 请求资源路径 21 fmt.Println(req.Body) // 请求体 22 23 w.Write([]byte("hello go")) 24 25 if req.Method == "POST"{ 26 buf := make([]byte, 2048) 27 n, _:=req.Body.Read(buf) 28 fmt.Println(string(buf[:n])) 29 } 30}
http客户端
1package main 2 3import ( 4 "net/http" 5 "fmt" 6) 7 8// Author: Wjy 9func main() { 10 response, err := http.Get("http://www.baidu.com") 11 if err != nil{ 12 fmt.Println(err) 13 return 14 } 15 fmt.Println(response.Status) // 例如"200 OK" 16 fmt.Println(response.StatusCode) // 例如200 17 fmt.Println(response.Proto) // 例如"HTTP/1.0" 18 fmt.Println(response.ProtoMajor ) // 例如1 19 fmt.Println(response.ProtoMinor) // 例如0 20 fmt.Println(response.Header) 21 fmt.Println(response.ContentLength ) 22 buf := make([]byte, 1024) 23 var s string 24 for { 25 n,_:=response.Body.Read(buf) 26 if n == 0 { 27 break 28 } 29 s+=string(buf[:n]) 30 } 31 fmt.Println(s) 32}
百度爬虫
1package main 2 3import ( 4 "fmt" 5 "net/http" 6 "regexp" 7 "sync" 8 9 "strings" 10 "os" 11 "io" 12 "time" 13) 14 15 16// Author: Wjy 17func main() { 18 // https://tieba.baidu.com/f?kw=%E5%9C%B0%E4%B8%8B%E5%9F%8E%E4%B8%8E%E5%8B%87%E5%A3%AB&ie=utf-8&pn=50 19 var mu sync.WaitGroup 20 var start, end int 21 22 fmt.Printf("输入其实页(>=1):") 23 fmt.Scan(&start) 24 fmt.Printf("输入终止页(>=起始页):") 25 fmt.Scan(&end) 26 start_time := time.Now().Unix() 27 DoWork(start, end, &mu) // 开始任务 28 fmt.Println("用时:",time.Now().Unix()-start_time) 29 30} 31 32func DoWork(start, end int, mu *sync.WaitGroup) { 33 fmt.Printf("正在爬取 %d 到 %d 的页面\n", start,end) 34 // 列表页url 35 base_url := `https://tieba.baidu.com/f?kw=地下城与勇士&ie=utf-8&pn=%d` 36 for i:=start;i<=end;i++{ 37 client := &http.Client{} 38 fmt.Println("第",i,"页") 39 mu.Add(1) 40 // 构建url 41 url := fmt.Sprintf(base_url,(i-1)*50) 42 // 构建get请求头 43 resp, _ := http.NewRequest("GET",url,nil) 44 resp.Header.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36") 45 // 执行请求 46 response, err := client.Do(resp) 47 if err != nil{ 48 fmt.Println("1",err) 49 continue 50 } 51 path := fmt.Sprintf("./dnf贴吧%d",i) 52 _, err = os.Stat(path) 53 if os.IsNotExist(err) { 54 os.Mkdir(path,os.ModePerm) 55 } 56 go HttpResponse(response, path, mu) // 解析列表页响应 57 } 58 mu.Wait() // 等待文件写入 59} 60 61func HttpResponse(response *http.Response, path string, mu *sync.WaitGroup) { 62 buf := make([]byte, 2048) 63 var body string 64 for { 65 n, err := response.Body.Read(buf) 66 if err == io.EOF || n == 0{ 67 break 68 } 69 body += string(buf[:n]) 70 } 71 response.Body.Close() 72 re, err := regexp.Compile(`href="(.*?)"`) 73 urls := re.FindAllStringSubmatch(body, -1) 74 if err != nil{ 75 fmt.Println("3",err) 76 } 77 //fmt.Println(urls) 78 count := 1 79 for _, v := range urls{ 80 if strings.HasPrefix(v[1], "/p"){ 81 mu.Add(1) 82 fmt.Println("正在下载第", count, "页") 83 go content(&v[1], path, mu) 84 fmt.Println("完成",count) 85 count++ 86 } 87 } 88 mu.Done() 89} 90 91func content(s *string,path string, mu *sync.WaitGroup) { 92 resp, err := http.Get("https://tieba.baidu.com"+*s) 93 if err != nil{ 94 fmt.Println("下载时出错", err) 95 return 96 } 97 buf := make([]byte, 2048) 98 var str string 99 for { 100 n, err := resp.Body.Read(buf) 101 if err == io.EOF || n == 0{ 102 break 103 } 104 str += string(buf[:n]) 105 } 106 resp.Body.Close() 107 re, err := regexp.Compile(`<title>(.*?)</title>`) 108 if err != nil{ 109 fmt.Println("标题未匹配:",err) 110 return 111 } 112 title := re.FindStringSubmatch(str) 113 f, _ := os.OpenFile(path+"/"+title[1]+".html",os.O_CREATE|os.O_WRONLY,os.ModePerm) 114 f.Write([]byte(str)) 115 f.Close() 116 mu.Done() 117}
反射详解 https://studygolang.com/articles/12348?fr=sidebar
interface 和 反射 在讲反射之前,先来看看Golang关于类型设计的一些原则
-
变量包括(type, value)两部分
- 理解这一点就知道为什么nil != nil了
-
type 包括 static type和concrete type. 简单来说 static type是你在编码是看见的类型(如int、string),concrete type是runtime系统看见的类型
-
类型断言能否成功,取决于变量的concrete type,而不是static type. 因此,一个 reader变量如果它的concrete type也实现了write方法的话,它也可以被类型断言为writer.
接下来要讲的反射,就是建立在类型之上的,Golang的指定类型的变量的类型是静态的(也就是指定int、string这些的变量,它的type是static type),在创建变量的时候就已经确定,反射主要与Golang的interface类型相关(它的type是concrete type),只有interface类型才有反射一说。
在Golang的实现中,每个interface变量都有一个对应pair,pair中记录了实际变量的值和类型:
(value, type)
value是实际变量值,type是实际变量的类型。一个interface{}类型的变量包含了2个指针,一个指针指向值的类型【对应concrete type】,另外一个指针指向实际的值【对应value】。
例如,创建类型为*os.File的变量,然后将其赋给一个接口变量r:
1tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) 2 3var r io.Reader 4r = tty
接口变量r的pair中将记录如下信息:(tty, *os.File),这个pair在接口变量的连续赋值过程中是不变的,将接口变量r赋给另一个接口变量w:
1var w io.Writer 2w = r.(io.Writer)
接口变量w的pair与r的pair相同,都是:(tty, *os.File),即使w是空接口类型,pair也是不变的。
interface及其pair的存在,是Golang中实现反射的前提,理解了pair,就更容易理解反射。反射就是用来检测存储在接口变量内部(值value;类型concrete type) pair对的一种机制。
Golang的反射reflect
reflect的基本功能TypeOf和ValueOf
既然反射就是用来检测存储在接口变量内部(值value;类型concrete type) pair对的一种机制。那么在Golang的reflect反射包中有什么样的方式可以让我们直接获取到变量内部的信息呢? 它提供了两种类型(或者说两个方法)让我们可以很容易的访问接口变量内容,分别是reflect.ValueOf() 和 reflect.TypeOf(),看看官方的解释
1// ValueOf returns a new Value initialized to the concrete value 2// stored in the interface i. ValueOf(nil) returns the zero 3func ValueOf(i interface{}) Value {...} 4 5翻译一下:ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0 6 7 8// TypeOf returns the reflection Type that represents the dynamic type of i. 9// If i is a nil interface value, TypeOf returns nil. 10func TypeOf(i interface{}) Type {...} 11 12翻译一下:TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil
reflect.TypeOf()是获取pair中的type,reflect.ValueOf()获取pair中的value,示例如下:
1package main 2 3import ( 4 "fmt" 5 "reflect" 6) 7 8func main() { 9 var num float64 = 1.2345 10 11 fmt.Println("type: ", reflect.TypeOf(num)) 12 fmt.Println("value: ", reflect.ValueOf(num)) 13} 14 15运行结果: 16type: float64 17value: 1.2345
说明
-
reflect.TypeOf: 直接给到了我们想要的type类型,如float64、int、各种pointer、struct 等等真实的类型
-
reflect.ValueOf:直接给到了我们想要的具体的值,如1.2345这个具体数值,或者类似&{1 "Allen.Wu" 25} 这样的结构体struct的值
-
也就是说明反射可以将“接口类型变量”转换为“反射类型对象”,反射类型指的是reflect.Type和reflect.Value这两种
从relfect.Value中获取接口interface的信息
当执行reflect.ValueOf(interface)之后,就得到了一个类型为”relfect.Value”变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型。不过,我们可能是已知原有类型,也有可能是未知原有类型,因此,下面分两种情况进行说明。
已知原有类型【进行“强制转换”】
已知类型后转换为其对应的类型的做法如下,直接通过Interface方法然后强制转换,如下:
realValue := value.Interface().(已知的类型)
示例如下:
1package main 2 3import ( 4 "fmt" 5 "reflect" 6) 7 8func main() { 9 var num float64 = 1.2345 10 11 pointer := reflect.ValueOf(&num) 12 value := reflect.ValueOf(num) 13 14 // 可以理解为“强制转换”,但是需要注意的时候,转换的时候,如果转换的类型不完全符合,则直接panic 15 // Golang 对类型要求非常严格,类型一定要完全符合 16 // 如下两个,一个是*float64,一个是float64,如果弄混,则会panic 17 convertPointer := pointer.Interface().(*float64) 18 convertValue := value.Interface().(float64) 19 20 fmt.Println(convertPointer) 21 fmt.Println(convertValue) 22} 23 24运行结果: 250xc42000e238 261.2345
说明
- 转换的时候,如果转换的类型不完全符合,则直接panic,类型要求非常严格!
- 转换的时候,要区分是指针还是指
- 也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
未知原有类型【遍历探测其Filed】
很多情况下,我们可能并不知道其具体类型,那么这个时候,该如何做呢?需要我们进行遍历探测其Filed来得知,示例如下:
1package main 2 3import ( 4 "fmt" 5 "reflect" 6) 7 8type User struct { 9 Id int 10 Name string 11 Age int 12} 13 14func (u User) ReflectCallFunc() { 15 fmt.Println("Allen.Wu ReflectCallFunc") 16} 17 18func main() { 19 20 user := User{1, "Allen.Wu", 25} 21 22 DoFiledAndMethod(user) 23 24} 25 26// 通过接口来获取任意参数,然后一一揭晓 27func DoFiledAndMethod(input interface{}) { 28 29 getType := reflect.TypeOf(input) 30 fmt.Println("get Type is :", getType.Name()) 31 32 getValue := reflect.ValueOf(input) 33 fmt.Println("get all Fields is:", getValue) 34 35 // 获取方法字段 36 // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历 37 // 2. 再通过reflect.Type的Field获取其Field 38 // 3. 最后通过Field的Interface()得到对应的value 39 for i := 0; i < getType.NumField(); i++ { 40 field := getType.Field(i) 41 value := getValue.Field(i).Interface() 42 fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value) 43 } 44 45 // 获取方法 46 // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历 47 for i := 0; i < getType.NumMethod(); i++ { 48 m := getType.Method(i) 49 fmt.Printf("%s: %v\n", m.Name, m.Type) 50 } 51} 52 53运行结果: 54get Type is : User 55get all Fields is: {1 Allen.Wu 25} 56Id: int = 1 57Name: string = Allen.Wu 58Age: int = 25 59ReflectCallFunc: func(main.User)
说明
通过运行结果可以得知获取未知类型的interface的具体变量及其类型的步骤为:
- 先获取interface的reflect.Type,然后通过NumField进行遍历
- 再通过reflect.Type的Field获取其Field
- 最后通过Field的Interface()得到对应的value
通过运行结果可以得知获取未知类型的interface的所属方法(函数)的步骤为:
- 先获取interface的reflect.Type,然后通过NumMethod进行遍历
- 再分别通过reflect.Type的Method获取对应的真实的方法(函数)
- 最后对结果取其Name和Type得知具体的方法名
- 也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
- struct 或者 struct 的嵌套都是一样的判断处理方式
通过reflect.Value设置实际变量的值
reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的。
示例如下:
1package main 2 3import ( 4 "fmt" 5 "reflect" 6) 7 8func main() { 9 10 var num float64 = 1.2345 11 fmt.Println("old value of pointer:", num) 12 13 // 通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值 14 pointer := reflect.ValueOf(&num) 15 newValue := pointer.Elem() 16 17 fmt.Println("type of pointer:", newValue.Type()) 18 fmt.Println("settability of pointer:", newValue.CanSet()) 19 20 // 重新赋值 21 newValue.SetFloat(77) 22 fmt.Println("new value of pointer:", num) 23 24 //////////////////// 25 // 如果reflect.ValueOf的参数不是指针,会如何? 26 pointer = reflect.ValueOf(num) 27 //newValue = pointer.Elem() // 如果非指针,这里直接panic,“panic: reflect: call of reflect.Value.Elem on float64 Value” 28} 29 30运行结果: 31old value of pointer: 1.2345 32type of pointer: float64 33settability of pointer: true 34new value of pointer: 77
说明
- 需要传入的参数是* float64这个指针,然后可以通过pointer.Elem()去获取所指向的Value,注意一定要是指针。
- 如果传入的参数不是指针,而是变量,那么
- 通过Elem获取原始值对应的对象则直接panic
- 通过CanSet方法查询是否可以设置返回false
- newValue.CantSet()表示是否可以重新设置其值,如果输出的是true则可修改,否则不能修改,修改完之后再进行打印发现真的已经修改了。
- reflect.Value.Elem() 表示获取原始值对应的反射对象,只有原始对象才能修改,当前反射对象是不能修改的
- 也就是说如果要修改反射类型对象,其值必须是“addressable”【对应的要传入的是指针,同时要通过Elem方法获取原始值对应的反射对象】
- struct 或者 struct 的嵌套都是一样的判断处理方式
通过reflect.ValueOf来进行方法的调用
这算是一个高级用法了,前面我们只说到对类型、变量的几种反射的用法,包括如何获取其值、其类型、如果重新设置新值。但是在工程应用中,另外一个常用并且属于高级的用法,就是通过reflect来进行方法【函数】的调用。比如我们要做框架工程的时候,需要可以随意扩展方法,或者说用户可以自定义方法,那么我们通过什么手段来扩展让用户能够自定义呢?关键点在于用户的自定义方法是未可知的,因此我们可以通过reflect来搞定
示例如下:
1// 反射取值 2val := v.Field(i).Interface()
1package main 2 3import ( 4 "fmt" 5 "reflect" 6) 7 8type User struct { 9 Id int 10 Name string 11 Age int 12} 13 14func (u User) ReflectCallFuncHasArgs(name string, age int) { 15 fmt.Println("ReflectCallFuncHasArgs name: ", name, ", age:", age, "and origal User.Name:", u.Name) 16} 17 18func (u User) ReflectCallFuncNoArgs() { 19 fmt.Println("ReflectCallFuncNoArgs") 20} 21 22// 如何通过反射来进行方法的调用? 23// 本来可以用u.ReflectCallFuncXXX直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是MethodByName,然后通过反射调动mv.Call 24 25func main() { 26 user := User{1, "Allen.Wu", 25} 27 28 // 1. 要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,得到“反射类型对象”后才能做下一步处理 29 getValue := reflect.ValueOf(user) 30 31 32 // 一定要指定参数为正确的方法名 33 // 2. 先看看带有参数的调用方法 34 methodValue := getValue.MethodByName("ReflectCallFuncHasArgs") 35 args := []reflect.Value{reflect.ValueOf("wudebao"), reflect.ValueOf(30)} 36 methodValue.Call(args) 37 38 // 一定要指定参数为正确的方法名 39 // 3. 再看看无参数的调用方法 40 methodValue = getValue.MethodByName("ReflectCallFuncNoArgs") 41 args = make([]reflect.Value, 0) 42 methodValue.Call(args) 43} 44 45 46运行结果: 47ReflectCallFuncHasArgs name: wudebao , age: 30 and origal User.Name: Allen.Wu 48ReflectCallFuncNoArgs
说明
-
要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,得到“反射类型对象”后才能做下一步处理
-
reflect.Value.MethodByName这.MethodByName,需要指定准确真实的方法名字,如果错误将直接panic,MethodByName返回一个函数值对应的reflect.Value方法的名字。
-
[]reflect.Value,这个是最终需要调用的方法的参数,可以没有或者一个或者多个,根据实际参数来定。
-
reflect.Value的 Call 这个方法,这个方法将最终调用真实的方法,参数务必保持一致,如果reflect.Value'Kind不是一个方法,那么将直接panic。
-
本来可以用u.ReflectCallFuncXXX直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是MethodByName,然后通过反射调用methodValue.Call
Golang的反射reflect性能
Golang的反射很慢,这个和它的API设计有关。在 java 里面,我们一般使用反射都是这样来弄的。
1Field field = clazz.getField("hello"); 2field.get(obj1); 3field.get(obj2);
这个取得的反射对象类型是 java.lang.reflect.Field。它是可以复用的。只要传入不同的obj,就可以取得这个obj上对应的 field。
但是Golang的反射不是这样设计的:
1type_ := reflect.TypeOf(obj) 2field, _ := type_.FieldByName("hello")
这里取出来的 field 对象是 reflect.StructField 类型,但是它没有办法用来取得对应对象上的值。如果要取值,得用另外一套对object,而不是type的反射
1type_ := reflect.ValueOf(obj) 2fieldValue := type_.FieldByName("hello")
这里取出来的 fieldValue 类型是 reflect.Value,它是一个具体的值,而不是一个可复用的反射对象了,每次反射都需要malloc这个reflect.Value结构体,并且还涉及到GC。
小结
Golang reflect慢主要有两个原因
-
涉及到内存分配以及后续的GC;
-
reflect实现里面有大量的枚举,也就是for循环,比如类型之类的。
总结
上述详细说明了Golang的反射reflect的各种功能和用法,都附带有相应的示例,相信能够在工程应用中进行相应实践,总结一下就是:
-
反射可以大大提高程序的灵活性,使得interface{}有更大的发挥余地
- 反射必须结合interface才玩得转
- 变量的type要是concrete type的(也就是interface变量)才有反射一说
-
反射可以将“接口类型变量”转换为“反射类型对象”
- 反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
-
反射可以将“反射类型对象”转换为“接口类型变量
- reflect.value.Interface().(已知的类型)
- 遍历reflect.Type的Field获取其Field
-
反射可以修改反射类型对象,但是其值必须是“addressable”
- 想要利用反射修改对象状态,前提是 interface.data 是 settable,即 pointer-interface
-
通过反射可以“动态”调用方法
-
因为Golang本身不支持模板,因此在以往需要使用模板的场景下往往就需要使用反射(reflect)来实现
1// 反射 2func main() { 3 //db, _ := sql.Open("goracle", "e_tyxb/e_tyxb@172.16.50.67:1521/orcl") 4 //db.SetMaxOpenConns(10) 5 //db.SetMaxIdleConns(1) 6 //defer db.Close() 7 //res, _ := db.Exec("select * from DEAL_ORDER_CUST_TYTT") 8 //fmt.Println(res) 9 u := User{1, "ok", 2} 10 //d := Manager{u, "no"} 11 Info(&u) 12} 13 14type User struct { 15 Id int 16 Name string 17 Age int 18} 19 20type Manager struct { 21 User 22 Title string 23} 24 25func (u User) Hello() { 26 fmt.Println("hello") 27} 28 29func Info(o interface{}) { 30 //t := reflect.TypeOf(o) 31 //fmt.Println("Type:", t.Name()) 32 33 //if k:=t.Kind();k!=reflect.Struct{ 34 // fmt.Println("xx") 35 //} 36 // 37 //v := reflect.ValueOf(o) 38 //fmt.Println("Fields:") 39 // 40 //for i:=0;i<t.NumField();i++{ 41 // //f := t.Field(i) 42 // val := v.Field(i).Interface() 43 // fmt.Println(val) 44 //} 45 46 //t := reflect.TypeOf(o) 47 //fmt.Println(t.FieldByIndex([]int{0, 1})) 48 49 v := reflect.ValueOf(o) 50 if v.Kind()==reflect.Ptr &&! v.Elem().CanSet(){ 51 return 52 } 53 v = v.Elem() 54 //f := v.FieldByName("Title") 55 if f:=v.FieldByName("Name");f.Kind()==reflect.String{ 56 57 f.SetString("byby") 58 } 59 fmt.Println(v) 60}
Log的使用
1const ( 2 dirpath = `E:\goland\src\测试目录` 3) 4var ( 5 log_ *log.Logger 6 errorfile = path.Join(dirpath, `\error.log.` + time.Now().Format("2006_01_02")) 7 infofile = path.Join(dirpath, `\info.log.` + time.Now().Format("2006_01_02")) 8 debugfile = path.Join(dirpath, `\debug.log.` + time.Now().Format("2006_01_02")) 9 warnfile = path.Join(dirpath, `\warn.log.` + time.Now().Format("2006_01_02")) 10 11 Debug = log.New(filepath(debugfile),"Debug: ",log.Ldate|log.Ltime|log.Lshortfile) 12 Error = log.New(filepath(errorfile),"Error: ",log.Ldate|log.Ltime|log.Lshortfile) 13 Info = log.New(filepath(infofile),"Info: ",log.Ldate|log.Ltime|log.Lshortfile) 14 Warn = log.New(filepath(warnfile),"Warn: ",log.Ldate|log.Ltime|log.Lshortfile) 15) 16// Author: Wjy 17 18func init() { 19 _, err:=os.Stat(dirpath) 20 if os.IsNotExist(err) { 21 os.MkdirAll(dirpath,os.ModePerm) 22 } 23}
