- fmt.Fprintf 向文件中写内容
- 格式化字符串
- 获取标准输入,输入字段以空格分割
- 获取标准输入,若用户输入包含空格,使用bufio实现
func main() {
// fmt.Fprintf 向文件中写内容
fileObj, err := os.OpenFile("./a.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer fileObj.Close()
s := "text"
fmt.Fprintf(fileObj, "%s\n", s)
// 格式化字符串
o := struct{ name string }{"zzdd"}
fmt.Printf("%v\n", o) // 值的默认格式
fmt.Printf("%+v\n", o) // 同时输出字段名
fmt.Printf("%#v\n", o) // 值的go语法
fmt.Printf("%T\n", o) // 值的类型
fmt.Printf("100%%\n") // 打印百分号
// 获取标准输入,输入字段以空格分割
// var (
// name string
// age int
// )
// fmt.Print("please input your name and age: ")
// fmt.Scan(&name, &age) // 注意这里的符号 &
// fmt.Printf("name: %s age: %d\n", name, age)
// 获取标准输入,若用户输入包含空格,使用bufio实现
reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
fmt.Print("请输入: ")
text, _ := reader.ReadString('\n') // 读到换行
text = strings.TrimSpace(text)
fmt.Printf("%#v\n", text)
}
/*
==========
{zzdd}
{name:zzdd}
struct { name string }{name:"zzdd"}
struct { name string }
100%
请输入: qq is good
"qq is good"
*/
标准库log
// 一个好的实践是将这部分通用配置放到init函数中
func init() {
logFile, err := os.OpenFile("./a.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
return
}
// 将日志写入文件
log.SetOutput(logFile)
// 控制输出日志信息的细节
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
// 配置日志信息前缀
log.SetPrefix("[prefix_name] ")
}
func main() {
// log.New 可以自定义日志输出
// func New(out io.Writer, prefix string, flag int) *Logger
logger := log.New(os.Stdout, "[prefix New] ", log.Lshortfile|log.Ldate|log.Ltime)
logger.Println("logger log")
log.Println("i am log")
v := "this is log"
log.Printf("%s", v)
// log.Fatalln("fatal") // Fatalln会在写入日志后调用os.Exit(1)
/*
[prefix New] 2021/03/16 09:57:35 main.go:27: logger log
cat a.log
[prefix_name] 2021/03/16 10:00:25.470469 main.go:28: i am log
[prefix_name] 2021/03/16 10:00:25.470494 main.go:30: this is log
*/
}
标准库time
时间类型输出
unix时间戳
定时器
格式化时间(Go的诞生时间2006年1月2号15点04分)
package main
import (
"fmt"
"time"
)
func main() {
// 时间类型
now := time.Now() // 当前时间
year := now.Year() // 当前年
month := now.Month() // 当前月
day := now.Day()
fmt.Println(now)
fmt.Printf("%d-%d-%d\n", year, month, day)
timestamp := now.Unix() // 当前unix时间戳
fmt.Printf("%v\n", timestamp)
// 时间戳
timeObj := time.Unix(timestamp, 0) // 将unix时间戳转换为时间
fmt.Println(timeObj)
hour := timeObj.Hour() //时
minute := timeObj.Minute() //分
second := timeObj.Second() //秒
fmt.Printf("%d:%d:%d\n", hour, minute, second)
// 定时器
// ticker := time.Tick(time.Second) // 间隔1秒
// for i := range ticker {
// fmt.Println(i)
// }
// 格式化时间,按照Go的诞生时间2006年1月2号15点04分
// 24小时制
fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan"))
// 12小时制
fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan"))
}
/*
========================
2021-03-15 18:18:32.977383 +0800 CST m=+0.000065185
2021-3-15
1615803512
2021-03-15 18:18:32 +0800 CST
18:18:32
2021-03-15 18:18:32.977 Mon Mar
2021-03-15 06:18:32.977 PM Mon Mar
*/
标准库strconv
strconv实现了基本数据类型和其字符串表示的相互转换。
- int类型转化为string类型
- str类型转化为int类型
- Parse函数:转换字符串为给定类型的值
- Format函数:将给定类型数据格式化为string类型
func main() {
// int类型转化为string类型
var i int = 4
var itostr string = strconv.Itoa(i)
fmt.Println(reflect.TypeOf(itostr), itostr)
// str类型转化为int类型
var str string = "5"
strtoi, err := strconv.Atoi(str)
if err != nil {
fmt.Println("convert failed")
}
fmt.Println(reflect.TypeOf(strtoi), strtoi)
// Parse函数用于转换字符串为给定类型的值
b1, err := strconv.ParseBool("true")
f1, err := strconv.ParseFloat("3.141", 64)
i1, err := strconv.ParseInt("-2", 10, 64)
u1, err := strconv.ParseUint("2", 10, 64)
fmt.Println(reflect.TypeOf(b1), b1)
fmt.Println(reflect.TypeOf(f1), f1)
fmt.Println(reflect.TypeOf(i1), i1)
fmt.Println(reflect.TypeOf(u1), u1)
//Format函数实现了将给定类型数据格式化为string类型数据的功能
s1 := strconv.FormatBool(true)
s2 := strconv.FormatFloat(3.1415, 'E', -1, 64)
s3 := strconv.FormatInt(-2, 16)
s4 := strconv.FormatUint(2, 16)
fmt.Println(reflect.TypeOf(s1), s1)
fmt.Println(reflect.TypeOf(s2), s2)
fmt.Println(reflect.TypeOf(s3), s3)
fmt.Println(reflect.TypeOf(s4), s4)
}
/*
string 4
int 5
bool
string true
bool true
float64 3.141
int64 -2
uint64 2
string true
string 3.1415E+00
string -2
string 2
*/
}
标准库 testing
单元测试
约定1:与测试的代码在同级目录,并以 “文件名_test.go” 命名
约定2:测试函数以Test开头
约定3:got want模式,有助于快速发现失败的原因
简单测试
a.go
func Greeting(s string) string {
return ("hello " + s)
}
a_test.go
func TestGreeting(t *testing.T) { // TestGreeting因为是Test开头,表明是一个测试,类型T包含很多用于测试代码的函数
got := Greeting("zzd") // got表示要测试的值
want := "hello zzd" // want表示期望的值
if got != want {
t.Fatalf("expected %v, got %v", want, got) // 错误友情提示
}
}
/*
终端执行
go test,显示测试结果,若将want内容修改为其它,则会打印错误友情提示
PASS
ok 06 0.017s
*/
单元测试覆盖率统计
go test -cover
PASS
coverage: 33.3% of statements
ok 06 0.017s
表格驱动测试
// 定义转化函数,匹配输入
func translate(s string) string {
switch s {
case "zh-gz":
return "hello "
case "zh-bj":
return "hi "
case "zh-sh":
return "good "
default:
return "hello "
}
}
// 定义got拼接
func Greeting(name, locale string) string {
salutation := translate(locale)
return (salutation + name)
}
// 使用表格驱动测试,能够同时测试很多条件
type GreetingTest struct {
name string
locale string
want string
}
// 表格数据赋值
var greetingTests = []GreetingTest{
{"aaa", "zh-gz", "hello aaa"},
{"ddd", "zh-bj", "hi ddd"},
{"ccc", "zh-sh", "good ccc"},
}
func TestGreeting(t *testing.T) { // TestGreeting因为是Test开头,表明是一个测试,类型T包含很多用于测试代码的函数
for _, test := range greetingTests {
got := Greeting(test.name, test.locale)
if got != test.want {
t.Errorf("Greeting(%s,%s)=%v;want %v", test.name, test.locale, got, test.want)
}
}
}
/*
go test
PASS
ok 06 0.017s
*/
性能(基准)测试
package temp
import (
"bytes"
"strings"
"testing"
)
// 字符串拼接几种方法性能(基准)测试,
// 1、直接赋值
func StringAssign(j int) string {
var s string
for i := 0; i < j; i++ {
s += "a"
}
return s
}
// 2、使用join赋值
func StringAppend(j int) string {
s := []string{}
for i := 0; i < j; i++ {
s = append(s, "a")
}
return strings.Join(s, "")
}
// 3、使用缓冲区赋值
func StringBuffer(j int) string {
var buffer bytes.Buffer
for i := 0; i < j; i++ {
buffer.WriteString("a")
}
return buffer.String()
}
// 性能(基准)测试需要以Benchmark打头,接受一个类型为B的参数,并对函数进行基准测试
func BenchmarkStringAssign(b *testing.B) {
for i := 0; i < b.N; i++ {
StringAssign(100)
}
}
func BenchmarkStringAppend(b *testing.B) {
for i := 0; i < b.N; i++ {
StringAppend(100)
}
}
func BenchmarkStringBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
StringBuffer(100)
}
}
/*
//终端执行,结论:缓冲区速度最快
go test -bench=.
BenchmarkStringAssign-12 284379 3896 ns/op
BenchmarkStringAppend-12 558100 1974 ns/op
BenchmarkStringBuffer-12 1902393 632 ns/op
PASS
ok 06 4.138s
*/
标准库 os
文件操作
// 将日志写入文件
f, err := os.OpenFile("stdout.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close() // defer延时关闭文件
log.SetOutput(f) // 通过SetOutput可以将标准输出直接输出到文件
var count = 5
for i := 0; i < count; i++ {
log.Printf("log iteration %d", i)
}
ioutil包用于执行一些常见的文件处理操作,底层使用os包,复杂操作可以用os包
/*
*/
type Config struct {
Name string `json:name`
Age float64 `json:age`
God bool `json:god`
}
func main() {
// 读取文件
fileBytes, err := ioutil.ReadFile("stdout.log") // 返回一个字节切片
if err != nil {
log.Fatal(err)
}
fileString := string(fileBytes) // 将字节切片转换为字符串
fmt.Println(fileString)
// 创建文件
b := make([]byte, 0) // WriteFile需接受字节切片,创建一个,并给文件赋权
err1 := ioutil.WriteFile("err.log", b, 0644)
if err1 != nil {
log.Fatal(err1)
}
// 将文本写入文件,文件不存在则创建
s := "hello world"
err2 := ioutil.WriteFile("err1.log", []byte(s), 0644)
if err != nil {
log.Fatal(err2)
}
// 列出当前目录内容,ReadDir返回列表,包含Name、Size等
files, err3 := ioutil.ReadDir(".")
if err3 != nil {
log.Fatal(err3)
}
for _, file := range files {
fmt.Println(file.Name(), file.Size(), file.IsDir(), file.Mode(), file.ModTime())
}
// 复制文件,使用os
from, err := os.Open("err1.log") // 读取文件
if err != nil {
log.Fatal(err)
}
defer from.Close() // 一定不要忘记关闭文件
to, err := os.OpenFile("err1.copy", os.O_RDWR|os.O_CREATE, 0666) // 打开文件
if err != nil {
log.Fatal(err)
}
defer to.Close() // 一定不要忘记关闭文件
_, err = io.Copy(to, from) // io.Copy(目标,来源)
if err != nil {
log.Fatal(err)
}
// 删除文件,需要先判断是否存在
// err5 := os.Remove("./err.log")
// if err5 != nil {
// log.Fatal(err5)
// }
// 从json文件中读取配置
f, err := ioutil.ReadFile("config.json")
if err != nil {
log.Fatal(err)
}
c := Config{}
err = json.Unmarshal(f, &c) // json解码
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", c.Name)
}
标准库 flag
简单命令行传参
func main() {
// 1.获取命令行参数os.Args
// for i, arg := range os.Args {
// fmt.Println("arg:", i, arg)
// }
// 2.获取命令行参数使用flag包
// flag.Usage自定义帮助文本
flag.Usage = func() {
usageText := `Usage cli [OPTION]
An example of customizing usage output
-s, --s string help text
-i, --i int help text
-b, --b bool help text
`
fmt.Fprintln(os.Stderr, usageText)
}
// flag.String表示接收string类型参数
s := flag.String("s", "hi", "string help text")
i := flag.Int("i", 1, "int help text")
b := flag.Bool("b", false, "bool help text")
//flag.Parse 传递声明的参数
flag.Parse()
fmt.Println("value of s:", *s)
fmt.Println("value of i:", *i)
fmt.Println("value of b:", *b)
}
复杂命令行参数(子命令)
func flagUsage() {
usageText := `script is a string handle cli tool.
Usage:
./script command [arguments]
The commands are:
upper
lower
Use "./script [command] --help" for more information about a command.
`
fmt.Fprintf(os.Stderr, "%s\n", usageText)
}
func main() {
// flag.Usage自定义帮助文本
flag.Usage = flagUsage
// 添加子命令flag.NewFlagSet
upperCmd := flag.NewFlagSet("upper", flag.ExitOnError)
lowerCmd := flag.NewFlagSet("lower", flag.ExitOnError)
// 当参数数量为1时,打印帮助文本
if len(os.Args) == 1 {
flag.Usage()
return
}
switch os.Args[1] {
case "upper": // 第一个参数为upper,若未指定-s,默认为空字符串
s := upperCmd.String("s", "", "to be upperd")
upperCmd.Parse(os.Args[2:])
fmt.Println(strings.ToUpper(*s))
case "lower":
s := lowerCmd.String("s", "", "to be lowerd")
lowerCmd.Parse(os.Args[2:])
fmt.Println(strings.ToLower(*s))
default:
flag.Usage() // 啥也不输入打印帮助信息
}
}
net/http
http服务端
// w r 意味着可查看或操作请求,再将响应返回给客户端,w生成响应
func greet(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Header.Get("Accept"), r.Method)
w.Header().Set("my-header", "i am setting a header") // 设置响应报头 // 设置自定义header
w.Header().Set("Content-Type", "application/json; charset=utf-8") // 同理,修改header直接覆盖就好
// fmt.Fprintf(w, "Hello World! %s", time.Now())
// 根据请求类型,进行不同操作
switch r.Header.Get("Accept") {
case "application/json":
fmt.Fprintf(w, "json")
case "application/xml":
fmt.Fprintf(w, "xml")
default:
fmt.Fprintf(w, "default")
}
}
func users(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "users! %s")
}
func main() {
http.HandleFunc("/", greet) // 路由信息处理
http.HandleFunc("/users/", users)
http.ListenAndServe(":8080", nil) // server监听配置
}
http客户端
func main() {
// 带参数的get请求
apiUrl := "https://www.baidu.com/"
// URL param
data := url.Values{}
data.Set("name", "zzd")
data.Set("age", "18")
u, err := url.ParseRequestURI(apiUrl)
if err != nil {
log.Fatal(err)
}
u.RawQuery = data.Encode() // URL encode 拼接字符串
fmt.Println(u.String())
resp, err := http.Get(u.String()) // 发送get请求
if err != nil {
log.Fatal(err)
return
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return
}
fmt.Println(string(b))
}
func main() {
// 发送post请求
postData := strings.NewReader(`{"some":"json"}`)
res, err := http.Post("http://127.0.0.1:8080", "application/json", postData)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", body)
func main() {
// 通过transport细粒度控制超时,http连接的各个阶段
tr := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 10 * time.Second,
IdleConnTimeout: 90 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
// 自定义客户端请求信息client.Do()
/*
设置报头、基本身份认证、cookie信息、发起其它方式的 HTTP 请求,比如 PUT、PATCH、DELETE 等
*/
debug := os.Getenv("DEBUG") // 读取环境变量
client := &http.Client{
Timeout: 1 * time.Second, // Timeout设置请求超时时间1s
Transport: tr, // Transport细粒度控制超时
}
request, err := http.NewRequest("GET", "http://127.0.0.1:8080", nil)
request.Header.Add("client-header", "i am client header") // 自定义client header
if err != nil {
log.Fatal(err)
}
if debug == "1" {
debugRequest, err := httputil.DumpRequestOut(request, true) //调试http显示请求信息
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", debugRequest)
}
response, err := client.Do(request) // 自定义请求信息
defer response.Body.Close()
if debug == "1" {
debugResponse, err := httputil.DumpResponse(response, true) // 调试http显示响应报头信息
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", debugResponse)
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", body)
}
/*
# go run main.go
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Go-http-client/1.1
Client-Header: i am client header // 客户端发起的自定义请求报头
Accept-Encoding: gzip
HTTP/1.1 200 OK
Content-Length: 7
Content-Type: application/json; charset=utf-8
Date: Mon, 15 Mar 2021 01:22:40 GMT
My-Header: i am setting a header // 服务端返回的自定义响应报头
*/
json编码解码
使用encoding/json中Marshal和Unmarshal
json编码和解码必须创建结构体,这样可以让代码更健壮,容错能力更强
json与go数据类型对应关系如下,因为go是强类型,需要做转换
json | go |
---|---|
Boolean | bool |
Number | float64 |
String | string |
Array | []interface{} |
Object | map[string]interface{} |
Null | nil |
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
// 使用encoding/json中Marshal和Unmarshal
// json编码和解码必须创建结构体,这样可以让代码更健壮,容错能力更强
type Person struct {
Name string `json:"name,omitempty"` // ``代表标签,Name重命名为name
Age int `json:"age,omitempty"`
Hobbies []string `json:"hobbies,omitempty"` // omitempty忽略零值,零值则不显示该字段
}
// 定义api返回数据的结构体
type User struct {
Name string `json:"name"`
Blog string `json:"blog"`
SiteAdmin bool `json:site_admin`
}
func main() {
// 使用Marshal编码json
p := Person{
Name: "zzd",
Age: 18,
Hobbies: []string{"basketball", "cheese"},
}
jsonByteData, err := json.Marshal(p) // 1. 编码成json byte
if err != nil {
log.Fatal(err)
}
jsonStringData := string(jsonByteData) // 2. 转化为string
fmt.Println("encode json:", jsonStringData)
// 使用Unmarshal解码json
jsonStringData1 := `{"name":"zzz", "age": 20, "hobbies":["ball", "cheese"]}`
jsonByteData1 := []byte(jsonStringData1)
p1 := Person{}
err1 := json.Unmarshal(jsonByteData1, &p1)
if err1 != nil {
log.Fatal(err1)
}
fmt.Printf("uncode json解码:%+v\n", p1)
// 处理http收到的json
var u User
res, err := http.Get("https://api.github.com/users/shapeshed")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
// fmt.Println(res)
err = json.NewDecoder(res.Body).Decode(&u) // 将数据流解码为结构体
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", u)
}
标准库math/rand
生成随机数
func random() {
rand.Seed(time.Now().UnixNano()) // 通过Seed获取时间戳,生成随机数
for i := 0; i < 5; i++ {
r := rand.Intn(10) // 10以内的随机数
fmt.Println(r, 0-r)
}
}
func main() {
random()
}