查看文件属性
package main
import (
"fmt"
"os"
)
func main() {
//打开文件使用os.Open函数,会返回一个文件句柄和一个error
file, err := os.Open("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("文件打开失败:",err)
}
defer file.Close()
// 文件的所在路径
fmt.Println(file.Name()) //I:\BaiduNetdiskDownload\yaoguai.txt
//拿到文件的描述符
fmt.Println(file.Fd()) // 296
//调用file.Stat()可以查看文件的信息,这是一个os.FileInfo对象
info, err := file.Stat()
//属性如下
/*
type FileInfo interface {
Name() string // 文件名
Size() int64 // 文件的大小,按字节计算
Mode() FileMode // 文件的模式
ModTime() time.Time // 修改时间
IsDir() bool // 是否是目录
Sys() interface{} // 数据源,一般不用管
}
*/
fmt.Println(info.Name()) // yaoguai.txt
fmt.Println(info.Size()) // 3870163
//有点类似于linux里面的,第一个-表示文本文件,后面的三个rw-表示可读可写不可执行。
//分别是用户、用户所属组、其他组的权限
fmt.Println(info.Mode()) // -rw-rw-rw-
fmt.Println(info.ModTime()) // 2020-06-29 19:21:33.9684636 +0800 CST
fmt.Println(info.IsDir()) // false
fmt.Println(info.Sys()) //&{32 {504531206 30821895} {1913938716 30821895} {1913938716 30821895} 0 3870163}
}
文件读取
文件读取:一次性读取所有文件内容
方法1:
package main
import (
"fmt"
"os"
)
func main() {
//打开文件使用os.Open函数,会返回一个文件句柄和一个error
file, err := os.Open("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("文件打开失败:",err)
return
}
defer file.Close()
info, err := file.Stat()
if err != nil {
fmt.Println("获取文件信息失败:",err)
return;
}
//读取文件内容可以调用file.Read()方法,接收一个字节数组,返回一个int和error
// buf的长度[ info.Size()] 和文件内容的关系
// * 长度 < info.Size() : 一次读不完,就读取 buf的长度 字节
// * 长度 == info.Size() : 全部读取
// * 长度 > info.size(): 浪费
buf := make([]byte, info.Size())
//此时文件的内容都会读到buf里面,n则是写入了多少个字节,n不会超过字节数组buf的长度
n, err := file.Read(buf)
//将写入的内容读取出来
fmt.Println(string(buf[:n])) //
}
方法2:
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, err := os.Open("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("文件打开失败:",err)
return
}
defer file.Close()
content, _ := ioutil.ReadAll(file)
fmt.Println(string(content))
fmt.Println(len(content)) // 字节的长度也为 3870163, 和 info.Size()获取到的长度是一样的
}
方法3:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
bytes, err := ioutil.ReadFile("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("error : %s", err)
return
}
content := string(bytes);
fmt.Println(content)
fmt.Println(len(content)) // 字节的长度也为 3870163, 和 info.Size()获取到的长度是一样的
}
文件读取:一次读取n个字节
package main
import (
"fmt"
"io"
"os"
)
func main() {
//打开文件使用os.Open函数,会返回一个文件句柄和一个error
file, err := os.Open("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("文件打开失败:",err)
return
}
defer file.Close()
buf := make([]byte, 100) 存放文件内容的缓存,相当于中转站
data := make([]byte, 0) // 用来存放文件内容,buf读取的内容都会写到data里面去
for{
n, err := file.Read(buf)
if err != nil {
//如果err != nil说明出错了,但如果还等于io.EOF的话,说明读完了,因为文件读完,err也不为nil。直接break
if err == io.EOF{
break
} else {
//如果错误不是io.EOF的话,说明就真的在读取中出现了错误,直接panic出来
panic(err)
}
}
data = append(data, buf[: n]...)
//我们来打印一下,每次写了多少个字节
fmt.Printf("写入%d个字节\n", n)
}
//将写入的内容读取出来
// fmt.Println(string(data))
fmt.Println(len(data)) //字节的长度也为 3870163, 和 info.Size()获取到的长度是一样的
}
写入文件: 追加写&&覆盖写
package main
import (
"fmt"
"os"
)
func main() {
//既然要写,那么就不能使用Open了,而是使用Create
//Create会创建一个文件,如果存在会清空原有的内容
file, err := os.Create(`aa.txt`)
if err != nil {
fmt.Println("文件创建失败:",err)
}
defer file.Close()
//写入文件可以调用file.WriteString,或者file.Write方法
//前者是写入字符,后者是写入字节。
// 其实WriteString底层是将我们传入的字符串变成字节数组,然后还是调用Write方法
//会有两个返回值,一个是我们写入的个数,一个是error
n, err := file.WriteString("这是新写入的内容,原来的内容会丢失")
//golang是按照字节算的,一个汉字占三个字节
fmt.Println(n) // 51
//再来读取一下看看,有没有写入
file, err = os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
//直接将数组设置的大一些,一次读取完毕
new_buf := make([]byte, 1024)
n, _ = file.Read(new_buf)
fmt.Println(string(new_buf[: n])) // 这是新写入的内容,原来的内容会丢失
}
其实无论是Open还是Create,底层都是调用了OpenFile
package main
import (
"fmt"
"os"
)
func main() {
// OpenFile接收三个参数
// 1.文件名
// 2.文件的模式,支持如下
/*
os.O_RDONLY: 以只读的方式打开
os.O_WRONLY: 以只写的方式打开
os.O_RDWR : 以读写的方式打开
os.O_NONBLOCK: 打开时不阻塞
os.O_APPEND: 以追加的方式打开
os.O_CREAT: 创建并打开一个新文件
os.O_TRUNC: 打开一个文件并截断它的长度为零(必须有写权限)
os.O_EXCL: 如果指定的文件存在,返回错误
os.O_SHLOCK: 自动获取共享锁
os.O_EXLOCK: 自动获取独立锁
os.O_DIRECT: 消除或减少缓存效果
os.O_FSYNC : 同步写入
os.O_NOFOLLOW: 不追踪软链接
*/
// 3.权限,一般设置为0666,这在linux下有用,Windows下面没太大卵用
// 以只写的方式打开,并且写入的时候,是以追加的形式写入
file, _ := os.OpenFile(`aa.txt`,os.O_WRONLY|os.O_APPEND, 0666)
_, _ = file.Write([]byte("\n这是新的内容,但是原来的内容还在"))
//写入之后,我们再以只读方式打开
file, _ = os.OpenFile(`aa.txt`,os.O_RDONLY, 0666)
buf := make([]byte, 1024)
n, _ := file.Read(buf)
fmt.Println(string(buf[: n]))
/*
这是新写入的内容,原来的内容会丢失
这是新的内容,但是原来的内容还在
*/
}
但是通常我们直接使用Open和Create函数
还可以使用ioutil写入文件:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
)
func main() {
buf := bytes.Buffer{}
buf.WriteString("aaaaaaaaaaaaaa\n")
buf.WriteString("bbbbbbbbbbbbbbbbb\n")
buf.WriteString("ccccccccccc");
//传入filename,content,权限
_ = ioutil.WriteFile(`aa.txt`, buf.Bytes(), 0666)
file, err := os.Open("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
fmt.Println("文件打开失败:",err)
return
}
defer file.Close()
//读取出来看看
s, _ := ioutil.ReadFile(`aa.txt`)
fmt.Println(string(s))
}
文件读取:按行读取
方法一:
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
err := HandleText("F:\\BaiduNetdiskDownload\\数据\\day2\\CSDNmail.txt")
if err != nil {
panic(err)
}
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
//使用bufio.NewReader进行一层包装
reader := bufio.NewReader(file)
for{
line,_,end:=reader.ReadLine() //读取一行数据
if end==io.EOF{ //文件关闭。跳出循环
break
}
fmt.Println(string(line))
}
return nil
}
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
err := HandleText("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
panic(err)
}
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
//使用bufio.NewReader进行一层包装
reader := bufio.NewReader(file)
//首先这个reader,可以像file一样,使用Read方法
//然而reader还可是按照指定字符来读,比如我想一行一行读,就可以指定换行符来读
for{
//返回string和error
s, err := reader.ReadString('\n') //表示每读到\n就停止
//这里使用print,因为文件本身有换行符,println自带换行,所以使用print
fmt.Print(s)
if err != nil {
if err == io.EOF{
break
} else {
panic(err)
}
}
}
return nil
}
方法二:
bufio相当于是在os.OpenFile得到的文件句柄之上进行一层封装,bufio,从名字上也能看出来是带缓存的io。
package main
import (
"os"
"log"
"fmt"
"bufio"
)
func main() {
err := HandleText("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
panic(err)
}
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
// scanner 用一次:循环之后scanner到了程序的最后面
for scanner.Scan() {
line := scanner.Text() // or
//line := scanner.Bytes()
//do_your_function(line)
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
log.Printf("Cannot scanner text file: %s, err: [%v]", textfile, err)
return err
}
return nil
}
bufio.Reader和bufio.Scanner的关系
bufio.Reader是go早期的版本也是用来处理文本,使用起来有一些不方便,例如需要处理行太长的问题,而bufio.Scanner是go1.1中新增加的功能,既然是新加的功能肯定是修正之前的不足,在使用上更加方便,比如就不用处理行太长的问题。
- NewScanner:scanner.Text()没有把换行符读进去
- NewReader:会换行符读进去
总之就是bufio.Scanner是后开发的模块,功能更强大,使用更方便。
单线程:统计一个文件中一共有多少个字符
package main
import (
"bufio"
"fmt"
"log"
"os"
"time"
"unicode"
)
var count = 0
func main() {
t := time.Now()
// "I:\\BaiduNetdiskDownload\\将嫁.txt"
// "I:\\BaiduNetdiskDownload\\妖怪食肆.txt"
err := HandleText("I:\\BaiduNetdiskDownload\\妖怪食肆.txt")
if err != nil {
panic(err)
}
elapsed := time.Since(t)
fmt.Println("count :", count)
fmt.Println("app elapsed:", elapsed)
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text() // or
for _, char := range line{
// 汉字 || 标点符号 || 数字 || 英文字母
if unicode.Is(unicode.Han, char) || unicode.IsPunct(char) || unicode.IsNumber(char) || unicode.IsLetter(char){
count++;
//fmt.Println("***", string(char))
}
}
/*fmt.Printf("%s\n", line)*/
}
if err := scanner.Err(); err != nil {
log.Printf("Cannot scanner text file: %s, err: [%v]", textfile, err)
return err
}
return nil
}
大文件分割
思路:
- 一般实现对文件进行分割操作,然后并发的做数据读入
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"sync"
"time"
)
func main() {
t := time.Now()
err := HandleText("I:\\BaiduNetdiskDownload\\yaoguai.txt")
if err != nil {
panic(err)
}
elapsed := time.Since(t)
fmt.Println("app elapsed:", elapsed)
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
ch := make(chan string)
wg := sync.WaitGroup{}
/*并发10个文件写入协程*/
for i := 0; i < 10 ; i++ {
wg.Add(1)
/*协程任务:从管道中拉取数据并写入到文件中*/
go func( i int) {
var index string
index = strconv.Itoa(i) + ".txt"
f, err := os.OpenFile(index,os.O_CREATE|os.O_WRONLY|os.O_TRUNC,0666)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", index, err)
return
}
defer f.Close()
totalLines := 0
for lineStr := range ch {
f.WriteString(lineStr)
//统计写出数量
totalLines ++
log.Printf("协程%d写入:%d",i,totalLines)
}
wg.Done()
}(i)
}
totalLines := 0
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text() // or
totalLines ++
println("读取数据:",totalLines)
ch <- line + "\n"
}
// 关闭数字通道
close(ch)
//阻塞等待所有协程结束任务
wg.Wait()
fmt.Println("file over!")
return nil
}
注意:分割开来的文本是乱序的
文件搜索
直接从硬盘中搜索
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strings"
"time"
)
func main() {
start_time := time.Now()
err := HandleText("F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt")
if err != nil {
panic(err)
}
fmt.Println("一共用了:" , time.Since(start_time))
/* 一共有 1 个oceanstar
一共用了: 4.1309617s*/
}
func HandleText(textfile string) error {
file, err := os.Open(textfile)
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", textfile, err)
return err
}
defer file.Close()
//使用bufio.NewReader进行一层包装
count := 0
reader := bufio.NewReader(file)
for{
line,_,end:=reader.ReadLine() //读取一行数据
if end==io.EOF{ //文件关闭。跳出循环
break
}
str := string(line)
if strings.Contains(str, "oceanstar") {
count++
}
}
fmt.Println("一共有" , count , "个oceanstar")
return nil
}
从硬盘中读取到内存数组然后查找
start_time := time.Now()
//-------------将文件读取到内存中------------
file, err := os.Open("F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt")
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", "F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt", err)
return
}
defer file.Close()
//使用bufio.NewReader进行一层包装
reader := bufio.NewReader(file)
strs_vector := make([]string, 0)
for{
line,_,end:=reader.ReadLine() //读取一行数据
if end==io.EOF{ //文件关闭。跳出循环
break
}
strs_vector = append(strs_vector, string(line))
}
fmt.Println("将硬盘数据一行行读取到内存数组中用时:" , time.Since(start_time))
for {
fmt.Println("请输入要查询的数据:")
search := "oceanstar"
fmt.Scanln(&search)
start_time = time.Now()
count := 0
for _, strs := range strs_vector {
if strings.Contains(strs, search) {
count++
//fmt.Println(strs)
}
}
fmt.Println("一共有" , count , "个oceanstar")
fmt.Println("从内存数组中搜索数据用时:" , time.Since(start_time))
}
/*
将硬盘数据一行行读取到内存中用时: 26.8531899s 【 11.6618142s, 这个时间是不确定的】
请输入要查询的数据:
oceanstar
一共有 1 个oceanstar
从内存数组中搜索数据用时: 1.2805488s
*/
从硬盘中读取到内存结构体然后查找
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"time"
)
type Qq struct {
QqNumber int
QqPassword string
}
func main() {
start_time := time.Now()
//-------------将文件读取到内存中------------
file, err := os.Open("F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt")
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", "F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
strs_vector := make([]Qq, 0)
for{
line,_,end:=reader.ReadLine() //读取一行数据
if end==io.EOF{ //文件关闭。跳出循环
break
}
// 处理数据
lineVec := strings.Split(string(line), "----")
if len(lineVec) == 2 {
num, _ := strconv.Atoi(lineVec[0])
strs_vector = append(strs_vector, Qq{
QqNumber: num,
QqPassword: lineVec[1],
})
}else{
fmt.Println(string(line), "错误的数据")
}
}
fmt.Println("将硬盘数据一行行读取到内存中用时:" , time.Since(start_time))
for {
fmt.Println("请输入要查询的数据:")
search := "oceanstar"
fmt.Scanln(&search)
start_time = time.Now()
count := 0
for _, strs := range strs_vector {
if strings.Contains(strs.QqPassword, search) {
count++
fmt.Println(strs)
}
}
fmt.Println("一共有" , count , "个oceanstar")
fmt.Println("从内存数组中搜索数据用时:" , time.Since(start_time))
}
/* 将硬盘数据一行行读取到内存中用时: 20.5460846s
请输入要查询的数据:
oceanstar
{51762051 oceanstar515980}
一共有 1 个oceanstar
从内存数组中搜索数据用时: 1.5847886s*/
}
从硬盘中读取到map然后查找
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"time"
)
func main() {
start_time := time.Now()
//-------------将文件读取到内存中------------
file, err := os.Open("F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt")
if err != nil {
log.Printf("Cannot open text file: %s, err: [%v]", "F:\\BaiduNetdiskDownload\\数据\\day2晚上\\QQ.txt", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
strs_map := make(map[string]int, 0)
for{
line,_,end:=reader.ReadLine() //读取一行数据
if end==io.EOF{ //文件关闭。跳出循环
break
}
// 处理数据
lineVec := strings.Split(string(line), "----")
if len(lineVec) == 2 {
num, _ := strconv.Atoi(lineVec[0])
strs_map[lineVec[1]] = num
}else{
fmt.Println(string(line), "错误的数据")
}
}
fmt.Println("将硬盘数据一行行读取到内存中用时:" , time.Since(start_time))
for {
fmt.Println("请输入要查询的数据:")
search := "oceanstar"
fmt.Scanln(&search)
start_time = time.Now()
count := 0
QQNum, err := strs_map[search]
if err{
fmt.Println(search,QQNum,"存在")
}else{
fmt.Println("找不到")
}
fmt.Println("一共有" , count , "个oceanstar")
fmt.Println("从内存数组中搜索数据用时:" , time.Since(start_time))
}
/* 将硬盘数据一行行读取到内存中用时: 1m2.548756s
请输入要查询的数据:
oceanstar
找不到
一共有 0 个oceanstar
从内存数组中搜索数据用时: 0s*/
}
操作 | 个数 (oceanstar) | 搜索用时 | 评价 |
---|---|---|---|
从硬盘中边一行行读取边搜索 | 1 | 4.1309617s | |
先将数据从硬盘读取到内存数组中,然后搜索 | 1 | 1.2805488s | 从硬盘读取数据并放入内存中耗时太久,好处是可以反复利用 |
从硬盘中读取到内存结构体然后查找 | 1 | 1.5847886s | 和上面用时差不多 |
从硬盘中读取到map然后查找 | 1 | 0 | 最快的,但是只能精确搜索 |
参考: https://www.cnblogs.com/traditional/p/11440728.html