go 标准输入输出 与 文件读写

平常使用的输入输出操作:

func main() {
    fmt.Println("输入输出测试")
    var a int
    var b string
    fmt.Scanln(&a, &b)	// 从终端读取字符串(输入时以空格分隔)
    fmt.Scanf("%d %s", &a, &b)	 // 格式化从终端读取(输入时以空格分隔)
	
    fmt.Println(a)		// 向终端打印
    fmt.Printf("a=%d, b=%s", a, b)	// 格式化向终端打印
}

其实, fmt.Fprintf()才是标准输出设备向控制台写数据的标准函数

如上的fmt.Printf(data)封装了fmt.Fprintf(io.Writer, data)

所以标准写法是: fmt.Fprinf(os.Stdout, data) -- os.Stdout表示控制台, 属于io.Writer类型

fmt.Fprintf()当然可以用于向文件写数据, 只需要修改os.Stdout 为*os.File类型的变量即可

func main() {
    // 创建一个文件变量
    file, err := os.OpenFile("c:/a.log", os.O_CREATE|os.O_WRONLY, 0)
    if err != nil {
        fmt.Println("打开文件失败")
        return
    }
    fmt.Fprintf(file, "abc123222")	// 向file对应文件中写入数据
    file.Close()
    fmt.Fprintf(os.Stdout, "abc123222")  // 向控制台(标准输出设备)输出数据
}

注: os.File封装所有文件相关的操作, 如下操作终端相关的常量都是 *os.File

os.stdin 标准输入

os.stdout 标准输出

os.stderr 标准错误输出

与fmt.Fprintf()对应的是fmt.Fscanf()

func main() {
    var s string 	
    fmt.Fscanf(os.Stdin, "%s", &s) 	// 根据空格读取
    fmt.Println(s)
	
    file, err := os.Open("c:/b.log")
    if err != nil {
        panic(err)
    }
    var a, b, c, d string 

    fmt.Fscanf(file, "%s", &a) 	
    fmt.Fscanf(file, "%s", &b)     // 根据空格分开读取
    fmt.Fscanf(file, "%s", &c)
    fmt.Fscanf(file, "%s", &d )
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}


另外, fmt.Sscanf(...) 函数可以从 字符串 中读取数据(相当于分解字符串, 比split强大)

第二个参数格式化目标字符串, 而split局限于单一分隔符

func main() {
    var (
        a, b int
        str, c string
    )
    str =  "10 20,abc"
    fmt.Sscanf(str, "%d %d,%s", &a, &b, &c) // 第一个是目标字符串, 第二个是格式化, 第三个是赋值
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}

最基础的读操作:

func main() {
    file, err := os.Open("c:/a.log")        // 读操作一般使用Open打开文件
    if err != nil {
        // 文件打开失败
        return
	}
	defer file.Close()    // 关文件
    var b []byte = make([]byte, 2*1024)    // 创建字节切片, 大小自己定
    byts , err := file.Read(b)             // 向file的文件中读取b大小的字节
    fmt.Println(byts)
}

最基础的写操作:

func main() {
    file, err := os.OpenFile("c:/b.log", os.O_CREATE|os.O_WRONLY, 0644)	// 写操作只能使用OpenFile打开文件
    if err != nil {
        // 文件打开失败
        return
    }
    defer file.Close()
    
    str := "my name is 张三, i am in China"
    var b = []byte(str)

    byts , err := file.Write(b)			// 将b大小的字节写入file的文件
    fmt.Println(byts)
}

带缓冲区的向文件写入数据:

它有三个参数, 第一个参数表示目标文件, 第二个参数写入方式, 第三个是权限

使用NewWriter创建*Writer, 再用WriterString写入文件

由于操作的是缓存区, 所以写入文件时, 需要Flush一下, 否则写入不成功

func main() {
    file, err := os.OpenFile("c:/a.log", os.O_CREATE|os.O_APPEND , 0666)
    defer file.Close()
    if err != nil {
        fmt.Println("打开文件失败", err)
    }
    writer := bufio.NewWriter(file)
    num, err2 := writer.WriteString("123")

    writer.Flush()	// 一定要使用flush, 否则很可能不能成功写入硬盘
    if err2 != nil {
        fmt.Println("文件写入失败", err2)
    }
    fmt.Println("写入字节数:", num)
}

带缓冲的读取文件

如果直接操作硬盘, 显然速度很慢

使用缓冲区, 则是先操作内存缓冲区, 后面才将数据刷到硬盘(不是直接操作硬盘), 这样可以大大提高效率

func main() {
    var reader *bufio.Reader = bufio.NewReader(os.Stdin)
    str, err := reader.ReadString('a')		// 需要的参数为一个字节, 表示以这个字节结束
    if err!= nil {
        fmt.Println("fail")
        return
    }
    fmt.Printf(str)
}

如果要读取一行, 则参数改为使用 '\n'

从文件读一行数据:

func main() {
    file, err := os.Open("c:/a.log")
    if err != nil {
        fmt.Println("打开文件失败", err)
        return
    }
    defer file.Close()            // 关文件, 只需要关原始的这个文件就行了
    var reader = bufio.NewReader(file)
    str, err2 := reader.ReadString('\n')		// 表示以换行符结束
    if err != nil {
        fmt.Println("读取文件失败", err2)
        return
    }
    fmt.Printf(str)
}

例: 统计一个文件中的所有大小写字母数量, 数字, 空格, 其它字符

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

/*抽象一个结构体, 用于表示 字母 数字 空格 其它字符*/
type Count struct {
    zimu int
    number int
    space int 
    other int
}

func main() {
    file, err := os.Open("c:/a.log")	// 打开文件
    if err != nil {
        fmt.Println("打开文件失败", err)
        return
    }
    defer file.Close()					// 关闭文件

    var reader = bufio.NewReader(file)	// 创建 *Reader
    var count *Count = &Count{}
    for {
        str, err2 := reader.ReadString('\n')	// 以行结尾
		
        if err2 == io.EOF {		// 读取到最后, 用io.EOF来判断
            fmt.Println("读取文件完成")
            break
        }
        if err2 != nil {
            fmt.Println("读取文件失败", err2)
            break
        }
				
        /*将str 转换为rune切片*/
        array:= []rune(str)
        for _, v := range array {
            switch {
            case v >='a' && v <='z':
                fallthrough
            case v >='A' && v<='Z':
                count.zimu ++
            case v >= '0' && v <='9':
                count.number ++
            case v == ' ' || v=='\t':
                count.space ++
            default:
                count.other ++
            }
        }
    }
    fmt.Println(*count)
}

读取压缩文件:

先打开压缩文件, 再根据压缩文件创建创建Reader

再使用bufio创建Reader, 这样就可以读取了

func main() {
    gfile, err := os.Open("c:/a.log.gz")	// 打开压缩文件
    defer gfile.Close()	
	
    if err != nil {
        fmt.Println("打开文件失败", err)
        return
    }

    greader, err := gzip.NewReader(gfile)	// 根据压缩文件, 创建greader
    var breader = bufio.NewReader(greader)	// 根据greader构建breader
	
    for {
        str, err2 := breader.ReadString('\n')	// 以行结尾
        if err2 == io.EOF {
            fmt.Println("读取文件结束")
            break
        }
        fmt.Println(str)
    }
}

文件拷贝:

io.Copy(dest, source) -- 传入两个参数Writer, Reader

func main() {
    sourcefile, err := os.Open("c:/a.log")
    if err != nil {
        // 文件打开失败
        return
	}
    defer sourcefile.Close()
    destfile, err2 := os.OpenFile("c:/b.log", os.O_CREATE|os.O_WRONLY, 0644)
    if err2 != nil {
        // 文件打开失败
        return
    }
    defer destfile.Close()

    io.Copy(destfile, sourcefile)
}

猜你喜欢

转载自blog.csdn.net/lljxk2008/article/details/87616977