一些练习题,后面的一些不常用的内容
排序、
一起学习
文件读写
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