golang:文件处理总结

查看文件属性

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)搜索用时评价
从硬盘中边一行行读取边搜索14.1309617s
先将数据从硬盘读取到内存数组中,然后搜索11.2805488s从硬盘读取数据并放入内存中耗时太久,好处是可以反复利用
从硬盘中读取到内存结构体然后查找11.5847886s和上面用时差不多
从硬盘中读取到map然后查找10最快的,但是只能精确搜索

参考: https://www.cnblogs.com/traditional/p/11440728.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值