Skip to content

Latest commit

 

History

History
571 lines (371 loc) · 9.58 KB

第十二章:读写数据.md

File metadata and controls

571 lines (371 loc) · 9.58 KB

第十二章:读写数据

前言

继续开始读写数据的学习,其实就是I/O流了估计是,这方面还是挺重要的。

读取用户的输入

Scanln 扫描来自标准输入的文本,将空格分隔的值依次存放到后续的参数内,直到碰到换行。Scanf 与其类似,除了 Scanf 的第一个参数用作格式字符串,用来决定如何读取。(感觉Go这方面类似C的scanf了)

package main

import "fmt"

func main() {
	fmt.Println("please input:")
	var firstName,lastName string
	var n1,n2 string
	fmt.Scanln(&firstName,&lastName)
	fmt.Printf("Hello %s %s\n",firstName,lastName)
	fmt.Scanf("%s,%s",&n1,&n2)
	fmt.Printf("%s,%s\n",n1,n2)
}

/*
please input:
a1 a2
Hello a1 a2
a1,a2
a1,a2,
 */

也可以用bufio包来读取:

package main

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

func main() {
	inputReader := bufio.NewReader(os.Stdin)
	fmt.Println("please input:")
	input, err := inputReader.ReadString('\n')
	if err != nil {
		fmt.Printf("input error!")
		return
	}
	fmt.Println(input)
	data := []byte(input)
	fmt.Println(data)//包括\n
}

ReadString 返回读取到的字符串,如果碰到错误则返回 nil。如果它一直读到文件结束,则返回读取到的字符串和 io.EOF。如果读取过程中没有碰到 delim 字符,将返回错误 err != nil。

文件读写

读文件

逐行读取文件:

package main

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

func main() {
	inputFile,inputError := os.Open("1.txt")
	if inputError!=nil {
		fmt.Println("file error!")
		return
	}
	fmt.Println(reflect.TypeOf(inputFile))
	defer inputFile.Close()

	inputReader := bufio.NewReader(inputFile)
	for {
		inputString,readerError := inputReader.ReadString('\n')
		fmt.Println(inputString)
		if readerError == io.EOF{
			return
		}

	}

}

也可以利用io/ioutilReadFile()函数讲整个文件的内容读取到一个[]byte中:

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	inputFile := "1.txt"
	buf,err := ioutil.ReadFile(inputFile)
	if err!=nil{
		fmt.Println("error")
		panic(err.Error())
	}
	fmt.Print(string(buf))

}

带缓冲的读取:

package main

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

func main() {
	inputFile,inputError := os.Open("1.txt")
	if inputError != nil {
		panic(inputError.Error())
	}
	defer inputFile.Close()
	inputReader := bufio.NewReader(inputFile)
	var buf []byte = make([]byte,1024)
	for {
		n,err := inputReader.Read(buf)
		if err==io.EOF{
			break
		}
		fmt.Print(string(buf[:n]))
	}


}

写文件

package main

import (
	"bufio"
	"os"
)

func main() {
	outputFile,_ := os.OpenFile("1.txt",os.O_WRONLY|os.O_CREATE, 0666)
	defer outputFile.Close()
	outputWriter := bufio.NewWriter(outputFile)
	outString := "hello,world\nwwwwww"
	outputWriter.WriteString(outString)
	outputWriter.Flush()
}

os.OpenFile的第二个参数是flag,有如下常用标志:

  • os.O_RDONLY:只读
  • os.O_WRONLY:只写
  • os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
  • os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为 0。

也可以不使用缓冲区,直接写入:

package main

import (
	"os"
)

func main() {
	outputFile,_ := os.OpenFile("1.txt",os.O_WRONLY|os.O_CREATE, 0666)
	defer outputFile.Close()
	outputFile.WriteString("hello,world")
}

练习:

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

type Page struct {
	Title string
	Body  []byte
}

func (page *Page) save(){
	outputFile,_ := os.OpenFile(page.Title,os.O_WRONLY|os.O_CREATE, 0666)
	defer outputFile.Close()
	outputFile.Write(page.Body)
}
func (page *Page) load(){
	page.Body,_ =ioutil.ReadFile(page.Title)
	fmt.Print(string(page.Body))
}

func main() {
	page := new(Page)
	page.Title = "2.txt"
	page.Body  = []byte("hello!!!!")
	page.save()
	page.load()
}

文件拷贝

利用io.Copy()

package main

import (
	"io"
	"os"
)

func main() {
	CopyFile("3.txt","1.txt")
}

func CopyFile(dstName,srcName string)(written int64,err error){
	src,err := os.Open(srcName)
	defer src.Close()
	dst,err := os.OpenFile(dstName,os.O_WRONLY|os.O_CREATE,0666)
	defer dst.Close()
	return io.Copy(dst,src)
}

defer 的使用:当打开目标文件时发生了错误,那么 defer 仍然能够确保 src.Close() 执行。如果不这么做,文件会一直保持打开状态并占用资源。

从命令行读取参数

os包

os.Args

这个命令行参数会放置在切片 os.Args[] 中(以空格分隔),从索引 1 开始(os.Args[0] 放的是程序本身的名字)。

flag包

感觉比os包解析命令行参数更好,而且用法更偏向于那些命令行参数的解析。

每个Flag结构体:

type Flag struct {
    Name     string // name as it appears on command line
    Usage    string // help message
    Value    Value  // value as set
    DefValue string // default value (as text); for usage message
}

指定参数:

var do = flag.String("w","no out","print")
//func String(name string, value string, usage string) *string

注意返回的是指针。

flag.Parse() 扫描参数列表(或者常量列表)并设置 flag, flag.Arg(i) 表示第 i 个参数。Parse() 之后 flag.Arg(i) 全部可用,flag.Arg(0) 就是第一个真实的 flag,而不是像 os.Args(0) 放置程序的名字。

flag.Narg() 返回参数的数量。解析后 flag 或常量就可用了。

flag.PrintDefaults() 打印 flag 的使用帮助信息。

需要注意的是,从第一个不能解析的参数开始,后面的所有参数都是无法解析的。即使后面的参数中含有预定义的参数。

例子:

package main

import (
	"flag"
	"fmt"
)

var do = flag.String("w","no out","print")

func main() {
	//flag.PrintDefaults()
	flag.Parse() // Scans the arg list and sets up flags
	fmt.Println(*do)
}
C:\Users\15997\Desktop>go run main.go -w 123
123

C:\Users\15997\Desktop>go run main.go
no out

C:\Users\15997\Desktop>go run main.go -w=123
123

C:\Users\15997\Desktop>

-help可以起到flag.PrintDefaults()的作用,不过它就只能打印帮助信息了,联想到之前使用工具中的-help,这部分还是挺重要的,接下来写工具就需要解析这些参数。

用切片读写文件

package main

import (
	"fmt"
	"os"
)

func cat(f *os.File){
	const NBUF = 1024
	var buf [NBUF]byte
	for {
		switch nr, _ := f.Read(buf[:]);true {
		case nr<0:
			fmt.Println("can not read")
			os.Exit(1)
		case nr==0://EOF
			return
		case nr>0:
			fmt.Print(string(buf[:nr]))
		}
	}
}

func main() {
	f,_ := os.Open("1.txt")
	defer f.Close()
	cat(f)
}

JSON数据格式

将对象转换为JSON:

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Person struct {
	Age int
	Name string
	Mate School
}
type School struct{
	Name string
	Position string
}

func main(){
	person := Person{18,"feng",School{"a","b"}}
	//得到字符串
	js,_ := json.Marshal(person)
	fmt.Printf("%s",js)

	//写入文件
	f,_ := os.OpenFile("1.json",os.O_WRONLY|os.O_CREATE,0666)
	defer f.Close()
	enc := json.NewEncoder(f)
	enc.Encode(person)
}

json.Marshal

注意结构体成员的首字母要大写才能成功转换。

出于安全考虑,在 web 应用中最好使用 json.MarshalforHTML() 函数,其对数据执行 HTML 转码,所以文本可以被安全地嵌在 HTML <script> 标签中。

不是所有的数据都可以编码为 JSON 类型:只有验证通过的数据结构才能被编码:

  • JSON 对象只支持字符串类型的 key;要编码一个 Go map 类型,map 必须是 map [string] T(T 是 json 包中支持的任何类型)

  • Channel,复杂类型和函数类型不能被编码

  • 不支持循环数据结构;它将引起序列化进入一个无限循环

  • 指针可以被编码,实际上是对指针指向的值进行编码(或者指针是 nil)

将JSON格式转换为对象:

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Age int
	Name string
	Mate School
}
type School struct{
	Name string
	Position string
}

func main(){
	person := Person{18,"feng",School{"a","b"}}
	//得到字符串
	js,_ := json.Marshal(person)
	fmt.Printf("%s\n",js)


	var p Person
	err := json.Unmarshal(js,&p)
	if err!=nil{
		panic(err.Error())
	}
	fmt.Println(p)


}

利用json.Unmarshalfunc Unmarshal(data []byte, v interface{}) error

第二个参数需要是指针。

Go中的密码学

Go也内置了许多的加密解密包。

  • hash 包:实现了 adler32、crc32、crc64 和 fnv 校验;

  • crypto 包:实现了其它的 hash 算法,比如 md4、md5、sha1 等。以及完整地实现了 aes、blowfish、rc4、rsa、xtea 等加密算法。

package main

import (
	"crypto/md5"
	"fmt"
	"io"
)

func main(){
	hasher := md5.New()
	io.WriteString(hasher,"feng")
	fmt.Printf("%x\n",hasher.Sum(nil))
	//

	hasher.Reset()
	data := []byte("hello")
	hasher.Write(data)
	fmt.Printf("%x\n",hasher.Sum(nil))

	fmt.Printf("%x",md5.Sum(data))
}