第十二章:读写数据
前言
继续开始读写数据的学习,其实就是I/O流了估计是,这方面还是挺重要的。
读取用户的输入
Scanln
扫描来自标准输入的文本,将空格分隔的值依次存放到后续的参数内,直到碰到换行。Scanf
与其类似,除了 Scanf
的第一个参数用作格式字符串,用来决定如何读取。(感觉Go这方面类似C的scanf了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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包来读取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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。
文件读写
读文件
逐行读取文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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/ioutil
的ReadFile()
函数讲整个文件的内容读取到一个[]byte
中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 ))
}
带缓冲的读取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 ]))
}
}
写文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。
也可以不使用缓冲区,直接写入:
1
2
3
4
5
6
7
8
9
10
11
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" )
}
练习:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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结构体:
1
2
3
4
5
6
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
}
指定参数:
1
2
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 的使用帮助信息。
需要注意的是,从第一个不能解析的参数开始,后面的所有参数都是无法解析的。即使后面的参数中含有预定义的参数。
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 )
}
1
2
3
4
5
6
7
8
9
10
C:\U sers\1 5997\D esktop>go run main.go -w 123
123
C:\U sers\1 5997\D esktop>go run main.go
no out
C:\U sers\1 5997\D esktop>go run main.go -w= 123
123
C:\U sers\1 5997\D esktop>
-help
可以起到flag.PrintDefaults()
的作用,不过它就只能打印帮助信息了,联想到之前使用工具中的-help,这部分还是挺重要的,接下来写工具就需要解析这些参数。
用切片读写文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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格式转换为对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.Unmarshal
:func Unmarshal(data []byte, v interface{}) error
第二个参数需要是指针。
Go中的密码学
Go也内置了许多的加密解密包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 ))
}