一.文件的基本介绍
文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档, txt 文件,excel文件 … 都是文件.文件最主要的作用就是保存数据,它既可以保存一张图片也可以保存视频,声音...
输入流和输出流
文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流: 数据从数据源(文件)到程序 ( 内存)的路径
输出流: 数据从程序(内存)到数据源(文件)的路径
os.File封装了所有与文件相关的操作,File是一个结构体
二.文件的基本操作
1.打开文件和关闭文件
package main
import (
"fmt"
"os"
)
func main() {
//打开文件(文件是一个指针类型)
//概念: file叫法: 1. file对象 2.file指针 3.file 文件句柄
file, err := os.Open("f:/www/test.txt")
if err != nil {
fmt.Printf("open file err = %v\n", err) //open filt err = %v open f:www/test.txt: The system cannot find the path specified.
}
//输出文件,可以看出:文件就是一个指针
fmt.Printf("file=%v\n", file) // 打开文件成功: file=&{0xc000088280}, 打开文件失败:file=<nil>
//关闭文件
err = file.Close()
if err != nil {
fmt.Println("close file err= %v", err)//close file err= %v invalid argument
}
}
2.读取文件操作
(1).读取文件的内容并显示在终端(带缓冲区的方法),使用os.Open,file.Close,bufio.NewReader,reader.ReadString函数和方法
package main
import (
"fmt"
"os"
"bufio"
"io"
)
func main() {
//打开文件(文件是一个指针类型)
//概念: file叫法: 1. file对象 2.file指针 3.file 文件句柄
file, err := os.Open("f:/www/test.txt")
if err != nil {
fmt.Printf("open file err = %v\n", err) //open filt err = %v open f:www/test.txt: The system cannot find the path specified.
}
//当函数退出时,关闭file,避免内存泄漏
defer file.Close()
//创建一个 *Reader,是带缓冲的
/**
* const (
* defaultBufSize = 4096 // 默认的缓冲区为4096
* )
*/
//NewReader创建一个具有默认大小缓冲、从r读取的*Reader
reader := bufio.NewReader(file)
//循环读取文件中的内容
for {
str, err := reader.ReadString('\n') //读到一个换行符结束
if err == io.EOF { //io.EOE 表示文件的末尾
break
}
//输出内容
fmt.Print(str)
}
fmt.Println("文件读取结束")
}
(2).读取文件的内容并显示在终端(使用ioutil一次性地将整个文件读入到内存中),这种方式适用于文件不大的情况,使用的相关方法和函数:ioutil.ReadFile
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//使用io/ioutil.ReadFile一次性将文件读取到位
file := "f:/www/test.txt"
//ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Printf("read file err = %v", err)
}
//把读取到文件内容显示到终端
// fmt.Printf("%v\n", content) //[]byte
fmt.Printf("%v\n", string(content)) //要显示正确的内容,需要把byte切片转换成string
//没有显示的Open文件,因此也不需要显示的Close文件,因为文件的Open,Close被封装到ReadFile函数内部
}
3.写文件操作
os.OpenFile函数介绍
第二个参数 flag:文件打开模式(可以组合)参数如下:
第三个参数 perm:权限控制参数如下:
r:读 ===> 4
w:写 ===> 2
x: 执行 ===> 1
方式一
(1).创建一个新文件,写入内容: 5句 "hello world"
package main
import (
"fmt"
"bufio"
"os"
)
func main() {
//创建一个新文件,写入内容:5聚 hello world
//打开一个文件 "f:/www/test2.txt"
filePath := "f:/www/test2.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666)//O_WRONLY 只写模式打开文件 ,O_CREATE 如果不存在将创建一个新文件,写的方式打开,新增
if err != nil {
fmt.Printf("open file err =%v\n", err)
}
//及时关闭
defer file.Close()
//准备写入5句内容
content := "hello world\r\n"
//NewWriter创建一个具有默认大小缓冲、写入w的*Writer
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
//WriteString写入一个字符串。返回写入的字节数。如果返回值nn < len(s),还会返回一个错误说明原因
writer.WriteString(content)
}
//因为Writer是带缓冲的,因此在调用WriterString方法时,其实内容是先写入到缓冲的
//所以需要调用Flush方法,将缓冲中的数据真正写入到文件中,否则文件中会丢失数据
//Flush方法将缓冲中的数据写入下层的io.Writer接口
writer.Flush()
}
(2).打开一个存在的文件,将原来的内容覆盖成新的内容,10句 "hello,你好"
package main
import (
"fmt"
"bufio"
"os"
)
func main() {
//打开一个存在的文件,将原来的内容覆盖成新的内容10句:你好,world
//打开一个存在文件 "f:/www/test2.txt"
filePath := "f:/www/test2.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC, 0666) //O_WRONLY 只写模式打开文件,O_TRUNC 打开时清空文件
if err != nil {
fmt.Printf("open file err =%v\n", err)
}
//及时关闭
defer file.Close()
//准备写入5句内容
content := "你好,world\r\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
//WriteString写入一个字符串。返回写入的字节数。如果返回值nn < len(s),还会返回一个错误说明原因
writer.WriteString(content)
}
//因为Writer是带缓冲的,因此在调用WriterString方法时,其实内容是先写入到缓冲的
//所以需要调用Flush方法,将缓冲中的数据真正写入到文件中,否则文件中会丢失数据
//Flush方法将缓冲中的数据写入下层的io.Writer接口
writer.Flush()
}
(3).打开一个文件,在原来的内容追加内容, 5句 "你好,world"
package main
import (
"fmt"
"bufio"
"os"
)
func main() {
//打开一个存在的文件,将原来的内容覆盖成新的内容10句:你好,world
//打开一个存在文件 "f:/www/test2.txt"
filePath := "f:/www/test2.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPENDC, 0666)//O_WRONLY
只写模式打开文件,O_APPENDC 写操作时将数据附加到文件尾部
if err != nil {
fmt.Printf("open file err =%v\n", err)
}
//及时关闭
defer file.Close()
//准备写入5句内容
content := "你好,world\r\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString(content)
}
//因为Writer是带缓冲的,因此在调用WriterString方法时,其实内容是先写入到缓冲的
//所以需要调用Flush方法,将缓冲中的数据真正写入到文件中,否则文件中会丢失数据
writer.Flush()
}
(4).打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句"你好,北京"
package main
import (
"fmt"
"bufio"
"os"
"io"
)
func main() {
//打开一个存在的文件,将原来的内容读写到终端,并追加内容10句:你好,北京
//打开一个存在文件 "f:/www/test2.txt"
filePath := "f:/www/test2.txt"
file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)//O_RDWR 读写模式打开文件, 写操作时将数据附加到文件尾部 O_APPEND
if err != nil {
fmt.Printf("open file err =%v\n", err)
}
//及时关闭
defer file.Close()
//先读取原来内容,并显示在终端
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF { // 如果读取到文件的末尾
break
}
//显示到终端
fmt.Print(str)
}
//准备写入5句内容
content := "你好,北京\r\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString(content)
}
//因为Writer是带缓冲的,因此在调用WriterString方法时,其实内容是先写入到缓冲的
//所以需要调用Flush方法,将缓冲中的数据真正写入到文件中,否则文件中会丢失数据
writer.Flush()
}
方式二
(1).案例:编写一个程序,将一个文件的内容,写入到另一个文件,注意:这两个文件已经存在了.
说明:使用ioutil.ReadFile,ioutil.WriterFile完成写文件的任务
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//将"f:/www/test2.txt" 内容导入 f:/www/test.txt"
//1.首先将test2.txt内容读取到内存
//2.将读取到内容写入f:/www/test.txt
filePath := "f:/www/test2.txt"
file2Path := "f:/www/test.txt"
//ReadFile 从filename指定的文件中读取数据并返回文件的内容
data, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Printf("read file err= %v", err)
return
}
//函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。
err = ioutil.WriteFile(file2Path, data, 0666)
if err != nil {
fmt.Println("writer file err= %v", err)
return
}
}
(2).判断文件/文件夹是否存在
判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
1).如果返回的错误为 nil ,说明文件或文件夹存在
2).如果返回的错误类型使用 os.IsNotExist()判断为 true ,说明文件或文件夹不存在
3).如果返回的错误为其它类型,则不确定是否在存在
func Stat
func Stat(name string) (fi FileInfo, err error)
//Stat返回一个描述name指定的文件对象的FileInfo。如果指定的文件对象是一个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,返回的错误值为*PathError类型。
func IsNotExist
func IsNotExist(err error) bool
//返回一个布尔值说明该错误是否表示一个文件或目录不存在。ErrNotExist和一些系统调用错误会使它返回真
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil { //文件或目录存在
return true, nil
}
if os.IsNotExists(err) {
return false, nil
}
return false, err
}
(3).拷贝文件
案例: 将一张图片/电影/mp3拷贝到另外一个文件e/abc.jpg
io包func Copy():
函数:
func Copy(dst Writer, src Reader) (written int64, err error)
说明:
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝
注意; copy’函数是 io 包提供的.
package main
import (
"fmt"
"io"
"os"
"bufio"
)
//自己编辑一个函数,接收两个文件路径srcFileName, dstFileName
func Copy(srcFileName string, dstFileName string) (written int64, err error) {
srcFile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open file err = %v\n", err)
}
defer srcFile.Close()
//通过srcFile,获取Reader
reader := bufio.NewReader(srcFile)
//打开dstFileName
dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err = %v\n", err)
return
}
//通过dstFile,获取Writer
writer := bufio.NewWriter(dstFile)
defer dstFile.Close()
return io.Copy(writer, reader)
}
func main() {
//将"f:/www/11.png" 拷贝到f:/下"
srcFile := "f:/www/11.png"
dstFile := "f:/11.png"
_, err := Copy(srcFile, dstFile)
if err == nil {
fmt.Println("拷贝完成")
} else {
fmt.Printf("拷贝错误,err = %v\n", err)
}
}
(4).统计英文,数字,空格和其他字符数量
说明:
统计一个文件中含有的英文,数字,空格及其他字符数量
package main
import (
"fmt"
"io"
"os"
"bufio"
)
//定义一个结构体,用于保存统计的结果
type CharCount struct {
ChCount int //记录英文个数
NumCount int //记录数字个数
SpaceCount int //记录空格个数
OtherCount int //记录其他字符个数
}
func main() {
//思路: 打开一个文件,创建一个Reader
//每读一行,就去统计该行有多少英文,数字,空格和其他字符
//然后将结果保存到一个结构体中
fileName := "f:/www/test3.txt"
file, err := os.Open(fileName)
if err != nil {
fmt.Printf("open file err = %v\n", err)
return
}
defer file.Close()
//定义一个CharCount实例
var count CharCount
//创建一个Reader
reader := bufio.NewReader(file)
//循环读取reader里的内容
for {
str, err := reader.ReadString('\n')
if err == io.EOF { //读到文件末尾就退出
break
}
//遍历str,进行统计
//为了兼容中文字符,可以将str转成[]rune
str = []rune(str)
for _, v := range str {
switch {
case v >= 'a' && v <= 'z':
fallthrough //穿透
case v >='A' && v <= 'Z':
count.ChCount++
case v == ' ' || v == '\t':
count.SpaceCount++
case v >='0' && v <= '9':
count.NumCount++
default:
count.OtherCount++
}
}
}
fmt.Printf("字符的个数%v,数字的个数%v,空格的个数%v,其他字符的个数%v\n",
count.ChCount, count.NumCount, count.SpaceCount, count.OtherCount)
}
三.命令行参数
基本介绍
os.Args是一个string切片,用来存储所有的命令行参数
var Args []string
Args保管了命令行参数,第一个是程序名
package main
import(
"fmt"
"os"
)
func main() {
fmt.Println("命令行的参数有:", len(os.Args))
//遍历os.Args切片,就可以得到所有的命令行输入参数值
for i, v := range os.Args {
fmt.Printf("args[%v]=%v\n", i, v)
}
}
PS F:\www\go-data\src\go_code\argsdemo> go build -o test.exe .\main.go
PS F:\www\go-data\src\go_code\argsdemo> .\test.exe tom d:/www 12
命令行的参数有: 4
args[0]=F:\www\go-data\src\go_code\argsdemo\test.exe
args[1]=tom
args[2]=d:/www
args[3]=12
flag包用来解析命令行参数
说明:前面的方式是比较原生的方式,对解析参数不是特别的方便,特别是带有指定参数形式的命令行.
比如:cmd>main.exe -f c:/aaa.txt -p 200 -u root 这样的形式命令行, go 设计者给我们提供了 fiag 包,可以方便的解析命令行参数,而且参数顺序可以随意
//请编写一段代码,可以获取命令行各个参数
D:/goproject/src/go_code>test.exe -u root -pwd root -h 192.168.1.1 -port 3306
user=root
pwd=root
host=192.168.1.1
port=3306
package main
import (
"fmt"
"flag"
)
func main() {
//定义几个变量,用于接收命令行参数值
var user string
var pwd string
var host string
var port int
//&user: 接收用户输入命令行中输入的 -u 后面的参数
//"u": 就是-u指定参数
//"":默认值
//"用户名,默认为空":参数说明
flag.StringVar(&user, "u", "", "用户名,默认为空")
flag.StringVar(&pwd, "pwd", "", "密码,默认为空")
flag.StringVar(&host, "host", "", "主机,默认为空")
flag.IntVar(&port, "port", 3306, "端口,默认为空")
//很重要的参数,转换,必须调用该方法
flag.Parse()
//输出结果
fmt.Printf("user=%v,pwd=%v,host=%v,port=%v", user, pwd, host, port)
}
#go build -o test.exe
#.\test.exe -u zhangs -pwd 123456
#user=zhangs,pwd=123456,host=,port=3306