Skip to content

Latest commit

 

History

History
707 lines (571 loc) · 11.9 KB

Chapter06IO.md

File metadata and controls

707 lines (571 loc) · 11.9 KB

Golang IO

File

os.File封装所有文件相关操作

操作终端相关文件句柄常量,它们都是都是*os.File

  • os.Stdin
  • os.Stdout
  • os.Stderr
const (
	O_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义
	O_WRONLY int = syscall.O_WRONLY // 只写打开文件
	O_RDWR   int = syscall.O_RDWR   // 读写方式打开文件
	O_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾
	O_CREATE int = syscall.O_CREAT  // 如果文件不存在,此案创建
	O_EXCL   int = syscall.O_EXCL   // 和O_CREATE一起使用, 只有当文件不存在时才创建
	O_SYNC   int = syscall.O_SYNC   // 以同步I/O方式打开文件,直接写入硬盘.
	O_TRUNC  int = syscall.O_TRUNC  // 如果可以的话,当打开文件时先清空文件
)

文件权限控制:

  • 4: r
  • 2: w
  • 1: x

example: write to terminal & file

package main

import (
	"fmt"
	"os"
)

func main() {
	// print to terminal
	fmt.Fprintln(os.Stdout, "hello")

	// print to file
	file, err := os.OpenFile("data.txt", os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("open file error!")
		return
	}
	fmt.Fprintf(file, "this is data.txt")
	file.Close()
}

example: Sscanf

package main

import (
	"fmt"
)

var (
	firstName, lastName, s string
	i                      int
	f                      float32
	input                  = "56.12 / 5212 / Go"
	format                 = "%f / %d / %s"
)

func main() {
	fmt.Println("Please enter your full name: ")
	// fmt.Scanf("%s %s", &firstName, &lastName)
	fmt.Scanln(&firstName, &lastName)              // 只能读string, 且以空格分隔
	fmt.Printf("Hi %s %s!\n", firstName, lastName) // Hi Chris Naegels

	fmt.Sscanf(input, format, &f, &i, &s)
	fmt.Println("From the string we read: ", f, i, s)
}

example Sscanf with struct

package main

import (
	"fmt"
)

type Student struct {
	Name  string
	Age   int
	Score float32
}

func main() {
	str1 := "stu01 24 56.5"
	stu1 := Student{}
	fmt.Sscanf(str1, "%s %d %f", &stu1.Name, &stu1.Age, &stu1.Score)
	fmt.Printf("%#v\n", stu1) //main.Student{Name:"stu01", Age:24, Score:56.5}
}

example: bufio Reader

带缓冲区的读写

package main

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

type Student struct {
	Name  string
	Age   int
	Score float32
}

func main() {
	inputReader := bufio.NewReader(os.Stdin)
	fmt.Println("enter you name:")
	input, err := inputReader.ReadString('\n')
	if err == nil {
		fmt.Println("your name:", input)
	}
}

example: bufio write

package main

import (
	"bufio"
	"os"
)

func main() {
	fi, _ := os.OpenFile("test.txt", os.O_WRONLY|os.O_CREATE, 0666)
	defer fi.Close()

	writer := bufio.NewWriter(fi)
	for i := 0; i < 10; i++ {
		writer.WriteString("hello, world\n")
	}
	writer.Flush() // bufio必须有
}

example: bufio write to terminal

package main

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

func main() {
	fmt.Fprintf(os.Stdout, "%s\n", "hello world! - unbuffered")
	buf := bufio.NewWriter(os.Stdout)
	fmt.Fprintf(buf, "%s\n", "hello world! - buffered")
	buf.Flush()
}
package main

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

type Student struct {
	Name  string
	Age   int
	Score float32
}

func main() {
	file, err := os.Open("data.txt")
	if err != nil {
		return
	}
	defer file.Close()

	inputReader := bufio.NewReader(file)
	str, err := inputReader.ReadString('\n') // read 1 line
	if err != nil {
		return
	}
	fmt.Println(str)
}

文件需要Close(), 如果在Linux只允许存在65535的文件句柄存在(通过ulimit -a查看))

example: 统计文件中英文、空格、数字、其他字符

package main

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

type CharCounter struct {
	Ch    int
	En    int
	Space int
	Num   int
	Other int
}

func (counter *CharCounter) Count(lines []string) {
	for _, line := range lines {
		for _, c := range line {
			if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
				counter.En++
			} else if c >= '0' && c <= '9' {
				counter.Num++
			} else if c == ' ' {
				counter.Space++
			} else if c >= 19968 && c <= 40869 {
				// Unicode汉字编码范围\u4E00-\u9FA5: 19968~40869
				counter.Ch++
			} else {
				counter.Other++
			}
		}
	}
	return
}

func main() {
	// open file
	file, err := os.Open("data.txt")
	if err != nil {
		return
	}
	defer file.Close()

	// bufio reader
	inputReader := bufio.NewReader(file)
	var lines []string
	for {
		str, err := inputReader.ReadString('\n') // read 1 line
		str = strings.TrimRight(str, "\r\n")     // for windows
		if err == io.EOF {
			// 补充最后一行
			lines = append(lines, str)
			break
		}
		if err != nil {
			fmt.Println("read file error", err)
			return
		}
		lines = append(lines, str)
	}
	counter := CharCounter{}
	counter.Count(lines)
	fmt.Printf("%#v\n", counter) // main.CharCounter{Ch:7, En:13, Space:4, Num:6, Other:4}
}

example: copy file by ioutil

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	buf, _ := ioutil.ReadFile("data.txt")
	fmt.Printf("%v", buf) // type is []byte

	err := ioutil.WriteFile("data_copy.txt", buf, 0x666)
	if err != nil {
		panic(err.Error())
	}
}

exampl: copy file by io.Copy()

  • 从文件到文件
  • 从文件到显示器
  • 从文件到网络
package main

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

func main() {

	CopyFile("target.txt", "source.txt")
	fmt.Println("Copy done!")
}

func CopyFile(dstName, srcName string) (written int64, err error) {
	src, err := os.Open(srcName)
	if err != nil {
		return
	}
	defer src.Close()

	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return
	}
	defer dst.Close()

	return io.Copy(dst, src)
}

example: read .gz

package main

import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"os"
)

func main() {
	fi, _ := os.Open("file1.gz") // gz内部只能有一个文件,多个文件需要先打包成tar
	fz, _ := gzip.NewReader(fi)
	r := bufio.NewReader(fz)
	for {
		line, err := r.ReadString('\n')
		fmt.Printf(line)
		if err == io.EOF {
			fmt.Println("\ndone")
			os.Exit(0)
		}
	}
}

example: os.Args是一个string的切片,用来存储所有的命令行参数

package main

import (
	"fmt"
	"os"
)

func main() {
	for _, v := range os.Args {
		fmt.Println(v)
	}
}

example: flag用来解析命令行参数

package main

import (
	"flag"
	"fmt"
)

func main() {
	var configPath string
	var logLevel int
	flag.StringVar(&configPath, "c", "", "please input config path")
	flag.IntVar(&logLevel, "d", 3, "please input log level")
	flag.Parse()
	fmt.Println(configPath, logLevel)
}
// in terminal > ./main.exe -c "c:/config" -d 6
// c:/config 6

example: read multi files

package main

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

func cat(r *bufio.Reader) {
	for {
		buf, err := r.ReadBytes('\n')
		fmt.Fprintf(os.Stdout, "%s", buf)
		if err == io.EOF {
			break
		}
	}
}

func main() {
	flag.Parse()
	fmt.Println(flag.Args())

	// command line没有参数从terminal读取
	if flag.NArg() == 0 {
		cat(bufio.NewReader(os.Stdin))
	}

	// command line有参数从文件读取
	for i := 0; i < flag.NArg(); i++ {
		f, err := os.Open(flag.Arg(i))
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s:error reading from %s: %s\n", os.Args[0], flag.Arg(i), err.Error())
			continue
		}
		cat(bufio.NewReader(f))
	}
}

json

exmaple: 序列化struct, map

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Age   int `json:"stu_age"`
	Score float32
}

func main() {
	stu1 := &Student{"alpha", 23, 12.5}

	// struct to json
	data1, _ := json.Marshal(stu1)
	fmt.Printf("%#v\n", data1)         // []byte
	fmt.Printf("%#v\n", string(data1)) // "{\"Name\":\"alpha\",\"stu_age\":23,\"Score\":12.5}"

	// map to json
	m1 := map[string]interface{}{"Name": "beta", "stu_age": 36, "Score": 13.6}
	data2, _ := json.Marshal(m1)
	fmt.Printf("%#v\n", string(data2)) // "{\"Name\":\"beta\",\"Score\":13.6,\"stu_age\":36}"
}

example: 反序列化struct, map

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Age   int `json:"stu_age"`
	Score float32
}

func main() {
	str := `{"Name":"alpha","Score":12.5,"stu_age":32}`

	// json to sturct
	stu1 := &Student{}
	json.Unmarshal([]byte(str), stu1)
	fmt.Printf("%#v\n", stu1) //&main.Student{Name:"alpha", Age:32, Score:12.5}

	// json to map
	m1 := map[string]interface{}{}
	json.Unmarshal([]byte(str), &m1)
	fmt.Printf("%#v\n", m1) // map[string]interface {}{"Name":"alpha", "Score":12.5, "stu_age":32}

	m2 := make(map[string]interface{})
	json.Unmarshal([]byte(str), &m2)
	fmt.Printf("%#v\n", m2) // map[string]interface {}{"Name":"alpha", "Score":12.5, "stu_age":32}

	var m3 map[string]interface{}
	json.Unmarshal([]byte(str), &m3)
	fmt.Printf("%#v\n", m3) // map[string]interface {}{"Name":"alpha", "Score":12.5, "stu_age":32}
}

example: 序列化slice

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Age   int `json:"stu_age"`
	Score float32
}

func main() {
	stu1 := Student{"alpha", 23, 12.5}
	stu2 := Student{"beta", 34, 12.5}
	var students []Student
	students = append(students, stu1, stu2)

	data, _ := json.Marshal(students)
	fmt.Printf("%#v\n", string(data)) // "[{\"Name\":\"alpha\",\"stu_age\":23,\"Score\":12.5},{\"Name\":\"beta\",\"stu_age\":34,\"Score\":12.5}]"
}

examle: 反序列化slice

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Age   int `json:"stu_age"`
	Score float32
}

func main() {
	str := `[{"Name":"alpha","stu_age":23,"Score":12.5},{"Name":"beta","stu_age":34,"Score":12.5}]`

	// json to slice of struct
	var students []Student
	json.Unmarshal([]byte(str), &students)
	fmt.Printf("%#v\n", students) // []main.Student{main.Student{Name:"alpha", Age:23, Score:12.5}, main.Student{Name:"beta", Age:34, Score:12.5}}

	// json to slice of map
	var stus []map[string]interface{}
	json.Unmarshal([]byte(str), &stus)
	fmt.Printf("%v\n", stus) // [map[Name:alpha Score:12.5 stu_age:23] map[Name:beta Score:12.5 stu_age:34]]
}

Error

golang中的error是如下接口

type error interface {
    Error() string
}

example: 自定义错误实现error的接口

package main

import (
	"fmt"
	"os"
	"time"
)

type PathError struct {
	Op         string
	Path       string
	Msg        string
	CreateTime string
}

func (e *PathError) Error() string {
	return fmt.Sprintf("%v|%v|%v|%v\n", e.Op, e.Path, e.Msg, e.CreateTime)
}

func Test(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return &PathError{"read", filename, err.Error(), time.Now().Format("2006/1/02")}
	}
	defer file.Close()
	return nil
}

func main() {
	err := Test("xxx.txt")
	if err != nil {
		fmt.Println(err.Error())
	}

	// type assertion method1
	v, ok := err.(*PathError)
	if ok {
		fmt.Printf("this is custom PathError:%#v\n", v)
		// this is custom PathError:&main.PathError{Op:"read", Path:"xxx.txt", Msg:"open xxx.txt: The system cannot find the file specified.", CreateTime:"2019/10/16"}
	}

	// type assertion method2
	switch v := err.(type) {
	case *PathError:
		fmt.Printf("this is custom PathError:%#v\n", v)
	default:

	}
}

example: 一般错误都不采用上述方法

package main

import (
	"errors"
	"fmt"
	"os"
)

var PathError = errors.New("PathError")

func Test(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return PathError
	}
	defer file.Close()
	return nil
}

func main() {
	err := Test("xxx.txt")
	if err != nil {
		fmt.Println(err)         // PathError
		fmt.Println(err.Error()) // PathError
	}
}

example: panic & recover

package main

import (
	"fmt"
)

func badCall() {
	panic("bad end")
}

func test() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("Panicking %s\n", e)
		}
	}()
	badCall()
	fmt.Printf("After bad call\r\n")
}

func main() {
	fmt.Printf("Calling test\r\n")
	test()
	fmt.Printf("Test completed\r\n")
}