go基础练习-下

 

一些练习题,后面的一些不常用的内容

排序、

一起学习:smiley:

文件读写

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	//read()
	write()
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func read() {
	dat, err := os.ReadFile("./tmp/dat")
	check(err)
	fmt.Print(string(dat))

	f, err := os.Open("./tmp/dat")
	check(err)

	b1 := make([]byte, 5) //申请五个数组
	n1, err := f.Read(b1)
	check(err)
	//读取前5个字符
	fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))

	//定位到第六个字符
	o2, err := f.Seek(6, 0)
	check(err)
	b2 := make([]byte, 2)
	n2, err := f.Read(b2)
	check(err)
	// 2 6
	fmt.Printf("%d bytes @ %d: ", n2, o2)
	// 第七个和第八个字符
	fmt.Printf("%v\n", string(b2[:n2]))

	o3, err := f.Seek(6, 0)
	check(err)
	b3 := make([]byte, 2)
	// 同样是读取两个字符(第六个字符后的两个)
	n3, err := io.ReadAtLeast(f, b3, 2)
	check(err)
	fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))

	_, err = f.Seek(0, 0)
	check(err)

	r4 := bufio.NewReader(f)
	b4, err := r4.Peek(5) //读取5个字符
	check(err)
	fmt.Printf("5 bytes: %s\n", string(b4))

	f.Close()
}

func write() {
	d1 := []byte("hello\ngo\n")
	err := os.WriteFile("./tmp/dat1", d1, 0644)//权限
	check(err)
	
	//创建文件
	f, err := os.Create("./tmp/dat2")
	check(err)
	
	//便捷的Close,怕忘记
	defer f.Close()
	
	d2 := []byte{115, 111, 109, 101, 10}
	n2, err := f.Write(d2)
	check(err)
	fmt.Printf("wrote %d bytes\n", n2)
	
	n3, err := f.WriteString("writes\n")
	check(err)
	fmt.Printf("wrote %d bytes\n", n3)
	
	//调用 Sync 将缓冲区的数据写入硬盘
	f.Sync()
	
	w := bufio.NewWriter(f)
	n4, err := w.WriteString("buffered\n")//\n也是一个字符
	check(err)
	fmt.Printf("wrote %d bytes\n", n4)
	
	//使用 Flush 来确保,已将所有的缓冲操作应用于底层 writer
	w.Flush()

	//追加写
	
	/**
	O_RDONLY:只读模式打开文件;
	O_WRONLY:只写模式打开文件;
	O_RDWR:读写模式打开文件;
	O_APPEND:写操作时将数据附加到文件尾部(追加);
	O_CREATE:如果不存在将创建一个新文件;
	O_EXCL:和 O_CREATE 配合使用,文件必须不存在,否则返回一个错误;
	O_SYNC:当进行一系列写操作时,每次都要等待上次的 I/O 操作完成再进行;
	O_TRUNC:如果可能,在打开时清空文件。
	 */
	file, err := os.OpenFile("./tmp/dat2", os.O_WRONLY|os.O_APPEND, 0666)
	check(err)
	defer file.Close()

	//写入文件时,使用带缓存的 *Writer
	write := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		write.WriteString("GO GO GO/ \n")
	}
	//Flush将缓存的文件真正写入到文件中
	write.Flush()
}

Linux 文件权限

权限 二进制 十进制 描述
- - - 000 0 无权限
- -x 001 1 只有执行权限
-w- 010 2 只有写入权限
-wx 011 3 写和执行权限
r- - 100 4 读权限
r-x 101 5 读取和执行的权限
rw- 110 6 读取和写入的权限
rwx 111 7 所有权限

行过滤器

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {

    scanner := bufio.NewScanner(os.Stdin)

    for scanner.Scan() {

        ucl := strings.ToUpper(scanner.Text())

        fmt.Println(ucl)
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "error:", err)
        os.Exit(1)
    }
}
$ echo 'hello'   > /tmp/lines
$ echo 'filter' >> /tmp/lines

$ cat /tmp/lines | go run line-filters.go
HELLO
FILTER

文件路径

/**
  @Go version: 1.17.6
  @project: TeachCode
  @ide: GoLand
  @file: filePath.go
  @author: Lido
  @time: 2022-03-06 15:57
  @description: 文件路径
*/
package main

import (
   "fmt"
   "path/filepath"
   "strings"
)

func main() {

   //拼接文件,提高可移植性,会自动矫正
   p := filepath.Join("dir1", "dir2", "filename")
   fmt.Println("p:", p)//dir1\dir2\filename


   fmt.Println(filepath.Join("dir1//", "filename"))       //dir1\filename
   fmt.Println(filepath.Join("dir1/../dir1", "filename"))//dir1\filename

   //路径
   fmt.Println("Dir(p):", filepath.Dir(p))//Dir(p): dir1\dir2
   //文件
   fmt.Println("Base(p):", filepath.Base(p))//Base(p): filename

   //判断是否是文件绝对路径
   fmt.Println(filepath.IsAbs("dir/file"))//false
   fmt.Println(filepath.IsAbs("C:/dir/file"))//true
   
   filename := "config.json"
   
   //文件的后缀
   ext := filepath.Ext(filename)
   fmt.Println(ext)//.json

   //文件的名称,不包括后缀
   fmt.Println(strings.TrimSuffix(filename, ext))//config

   //路径的相对路径
   rel, err := filepath.Rel("a/b", "a/b/t/file")
   if err != nil {
      panic(err)
   }
   fmt.Println(rel)//t/file

   rel, err = filepath.Rel("a/b", "a/c/t/file")
   if err != nil {
      panic(err)
   }
   fmt.Println(rel)//..\c\t\file
}

文件和目录

package main

import (
   "fmt"
   "os"
   "path/filepath"
)

func _check(e error) {
   if e != nil {
      panic(e)
   }
}

func main() {

   //在当前工作目录下,创建一个子目录。
   err := os.Mkdir("subdir", 0755)
   _check(err)

   //os.RemoveAll 会删除整个目录(类似于 rm -rf)
   defer os.RemoveAll("subdir")

   createEmptyFile := func(name string) {
      d := []byte("")
      //创建文件
      _check(os.WriteFile(name, d, 0644))
   }

   createEmptyFile("subdir/file1")

   //这个类似于命令 mkdir -p。
   err = os.MkdirAll("subdir/parent/child", 0755)
   _check(err)

   createEmptyFile("subdir/parent/file2")
   createEmptyFile("subdir/parent/file3")
   createEmptyFile("subdir/parent/child/file4")

   //列出目录的内容
   c, err := os.ReadDir("subdir/parent")
   _check(err)

   fmt.Println("Listing subdir/parent")
   for _, entry := range c {
      fmt.Println(" ", entry.Name(), entry.IsDir())
   }

   //Chdir 可以修改当前工作目录,类似于 cd
   err = os.Chdir("subdir/parent/child")
   _check(err)

   //当我们列出 当前 目录
   c, err = os.ReadDir(".")
   _check(err)

   fmt.Println("Listing subdir/parent/child")
   for _, entry := range c {
      fmt.Println(" ", entry.Name(), entry.IsDir())
   }

   err = os.Chdir("../../..")
   _check(err)

   fmt.Println("Visiting subdir")
   //遍历整个目录
   //filepath.Walk 遍历访问到每一个目录和文件后,都会调用 visit
   err = filepath.Walk("subdir", visit)
}

func visit(p string, info os.FileInfo, err error) error {
   if err != nil {
      return err
   }
   fmt.Println(" ", p, info.IsDir())
   return nil
}
Listing subdir/parent
  child true
  file2 false
  file3 false
Listing subdir/parent/child
  file4 false
Visiting subdir
  subdir true
  subdir\file1 false
  subdir\parent true
  subdir\parent\child true
  subdir\parent\child\file4 false
  subdir\parent\file2 false
  subdir\parent\file3 false

临时目录和文件

package main

import (
   "fmt"
   "os"
   "path/filepath"
)

func __check(e error) {
   if e != nil {
      panic(e)
   }
}

func main() {

   //"" 系统临时变量存放的地方
   f, err := os.CreateTemp("", "sample")
   __check(err)

   fmt.Println("Temp file name:", f.Name())

   defer os.Remove(f.Name())

   //向文件中写入数据
   _, err = f.Write([]byte{1, 2, 3, 4})
   __check(err)

   //读取文件
   dat, err := os.ReadFile(f.Name())
   __check(err)
   fmt.Println(dat)

   //创建临时文件夹
   dname, err := os.MkdirTemp("", "sampledir")
   fmt.Println("Temp dir name:", dname)

   defer os.RemoveAll(dname)

   //在临时目录中创建文件和写入数据
   fname := filepath.Join(dname, "file1")
   err = os.WriteFile(fname, []byte{1, 2}, 0666)
   __check(err)

   //输出文件内容
   dat, err = os.ReadFile(fname)
   __check(err)
   fmt.Println(fname,":",dat)
}
Temp file name: C:\Users\***\AppData\Local\Temp\sample3595232790
[1 2 3 4]
Temp dir name: C:\Users\***\AppData\Local\Temp\sampledir1992099884
C:\Users\Lido\AppData\Local\Temp\sampledir1992099884\file1  :  [1 2]

命令行参数

[0] 是程序名称

package main

import (
	"fmt"
	"os"
)

func main() {

	argsWithProg := os.Args
	argsWithoutProg := os.Args[1:]

	arg := os.Args[3]
	arg2 := os.Args[0]

	fmt.Println(argsWithProg)
	fmt.Println(argsWithoutProg)
	fmt.Println(arg)
	fmt.Println(arg2)
}
F:\GOCODE\TeachCode>TEST.exe a b c
[TEST.exe a b c]
[a b c]
c
TEST.exe

命令行标志

package main

import (
    "flag"
    "fmt"
)

func main() {

    wordPtr := flag.String("word", "foo", "a string")

    numbPtr := flag.Int("numb", 42, "an int")
    forkPtr := flag.Bool("fork", false, "a bool")

    var svar string
    flag.StringVar(&svar, "svar", "bar", "a string var")

    flag.Parse()

    fmt.Println("word:", *wordPtr)
    fmt.Println("numb:", *numbPtr)
    fmt.Println("fork:", *forkPtr)
    fmt.Println("svar:", svar)
    fmt.Println("tail:", flag.Args())
}
测试这个程序前,最好将这个程序编译成二进制文件,然后再运行这个程序。 $ go build command-line-flags.go
首先以给所有标志赋值的方式,尝试运行构建的程序。 $ ./command-line-flags -word=opt -numb=7 -fork -svar=flag word: opt numb: 7 fork: true svar: flag tail: []
注意,如果你省略一个标志,那么这个标志的值自动的设定为他的默认值。 $ ./command-line-flags -word=opt word: opt numb: 42 fork: false svar: bar tail: []
尾随的位置参数可以出现在任何标志后面。 $ ./command-line-flags -word=opt a1 a2 a3 word: opt ... tail: [a1 a2 a3]
注意,flag 包需要所有的标志出现位置参数之前(否则,这个标志将会被解析为位置参数)。 $ ./command-line-flags -word=opt a1 a2 a3 -numb=7 word: opt numb: 42 fork: false svar: bar tail: [a1 a2 a3 -numb=7]
使用 -h 或者 --help 标志来得到自动生成的这个命令行程序的帮助文本。 $ ./command-line-flags -h Usage of ./command-line-flags: -fork=false: a bool -numb=42: an int -svar="bar": a string var -word="foo": a string
如果你提供了一个没有使用 flag 包声明的标志, 程序会输出一个错误信息,并再次显示帮助文本。 $ ./command-line-flags -wat flag provided but not defined: -wat Usage of ./command-line-flags: ...

命令行子命令

package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {

    fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
    fooEnable := fooCmd.Bool("enable", false, "enable")
    fooName := fooCmd.String("name", "", "name")

    barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
    barLevel := barCmd.Int("level", 0, "level")

    if len(os.Args) < 2 {
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }

    switch os.Args[1] {

    case "foo":
        fooCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'foo'")
        fmt.Println("  enable:", *fooEnable)
        fmt.Println("  name:", *fooName)
        fmt.Println("  tail:", fooCmd.Args())
    case "bar":
        barCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'bar'")
        fmt.Println("  level:", *barLevel)
        fmt.Println("  tail:", barCmd.Args())
    default:
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }
}
  $ go build command-line-subcommands.go
首先调用 foo 子命令。 $ ./command-line-subcommands foo -enable -name=joe a1 a2 subcommand 'foo' enable: true name: joe tail: [a1 a2]
然后试一下 bar 子命令。 $ ./command-line-subcommands bar -level 8 a1 subcommand 'bar' level: 8 tail: [a1]
但是 bar 不接受 foo 的 flag(enable)。 $ ./command-line-subcommands bar -enable a1 flag provided but not defined: -enable Usage of bar: -level int level

环境变量

package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {

    os.Setenv("FOO", "1")
    fmt.Println("FOO:", os.Getenv("FOO"))
    fmt.Println("BAR:", os.Getenv("BAR"))

    fmt.Println()
    for _, e := range os.Environ() {
        pair := strings.SplitN(e, "=", 2)
        fmt.Println(pair[0])
    }
}

HTTP

HTTP 客户端

package main

import (
   "bufio"
   "fmt"
   "net/http"
)

func main() {

   resp, err := http.Get("http://gobyexample.com")
   if err != nil {
      panic(err)
   }
   defer resp.Body.Close()

   fmt.Println("Response status:", resp.Status)

   scanner := bufio.NewScanner(resp.Body)
   //读取6行
   for i := 0; scanner.Scan() && i < 5; i++ {
      fmt.Println(scanner.Text())
   }

   if err := scanner.Err(); err != nil {
      panic(err)
   }
}
Response status: 200 OK
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Go by Example</title>

HTTP 服务端

package main

import (
	"fmt"
	"net/http"
)

func _hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {
	//name header名称
	for name, headers := range req.Header {
		//h是header名称下的具体内容
		for _, h := range headers {
			fmt.Fprintf(w, "%v---%v\n", name,h)
		}
	}
}

func main() {

	http.HandleFunc("/hello", _hello)
	http.HandleFunc("/headers", headers)

	http.ListenAndServe(":8090", nil)
}

Context

package main

import (
	"fmt"
	"net/http"
	"time"
)

func _hello(w http.ResponseWriter, req *http.Request) {

	ctx := req.Context()
	fmt.Println("server: hello handler started")
	defer fmt.Println("server: hello handler ended")

	select {
	case <-time.After(5 * time.Second):
		fmt.Fprintf(w, "hello----------------\n")
	case <-ctx.Done():
		err := ctx.Err()
		fmt.Println("server:", err)
		internalError := http.StatusInternalServerError
		http.Error(w, err.Error(), internalError)
	}
}

func main() {

	http.HandleFunc("/hello", _hello)
	http.ListenAndServe(":8090", nil)
}
F:\GOCODE\TeachCode>curl localhost:8090/hello
hello----------------

F:\GOCODE\TeachCode>curl localhost:8090/hello
^C
F:\GOCODE\TeachCode>TEST.exe &
server: hello handler started
server: context canceled
server: hello handler ended

信号

可以做优雅退出

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {

    sigs := make(chan os.Signal, 1)

    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    done := make(chan bool, 1)

    go func() {

        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}
$ go run signals.go
awaiting signal
^C
interrupt
exiting

退出

package main

import (
	"fmt"
	"os"
)

func main() {

	defer fmt.Println("!")

	os.Exit(3)
}

直接运行,没有结果,比较生硬


在命令行运行,会显示退出状态

F:\GOCODE\TeachCode>go run TEST.go
exit status 3

参考资料

Go by Example 中文版