首页 > 其他分享 >文本处理

文本处理

时间:2024-03-06 10:45:19浏览次数:16  
标签:文件 err fmt 文本处理 file Println os

目录

文本处理

  • io/ioutil、os、bufio 是Go中最常用的文本处理的库
    • io/ioutil、os库都支持打开文件、读取文件、创建文件、写入文件、关闭文件
    • bufio库只支持读取文件和写入文件、创建文件,不支持打开文件和关闭文件

一、文件打开与关闭

  • Go语言没有python中的with 这种上下文管理的关键字,所以我们要注意打开文件之后要记得用defer手动关闭文件
  • 共3中打开文件的方式

1. os.open打开

  • 使用os.Open()os.OpenFile()打开文件(Open本质还是调用了OpenFile
  • 需要用defer手动关闭文件
函数定义:
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

- 可以看出,Open()函数默认为只读,只接收了一个文件路径参数(支持相对路径)

- 示例:

func main() {
	file,err:=os.Open("./xx.txt") // 打开文件,本质是调用了 OpenFile(name, O_RDONLY, 0)
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	fmt.Println(file) // 文件句柄
}

2. os.OpenFile打开(推荐)

  • 需要用defer手动关闭文件
函数定义:
func OpenFile(name string, flag int, perm FileMode) (*File, error)

- flag:打开模式
- perm:打开权限

示例:

func main() {

	file,err:=os.OpenFile("./xx.txt",os.O_RDONLY,0) // 打开文件
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	fmt.Println(file) // 文件句柄
}

(1)flag参数

模式 说明
os.O_WRONLY 只写
os.O_CREATE 创建文件
os.O_RDONLY 只读
os.O_RDWR 读写
os.O_TRUNC 清空
os.O_APPEND 追加

(2)prem参数(文件权限)

  • 同Linux系统中文件的权限功能相同,点击查看详情:十七、权限相关
  • 下面简要说下
# 关于- rwx rwx rwx:
第一个 - :代表这是一个普通文件(regular), 其中其他文件类型还包括了
d--------->目录文件(directory)
l --------->链接文件(link)
b--------->块设备文件(block)
c--------->字符设备文件(character)
s--------->套接字文件(socket)
p--------->管道文件(pipe)


# 一共有三组rwx(下图中的每一个框起来的rwx对应一组用户权限),分别是以下几种用户的rwx权限:
第一组:该文件拥有者的权限
第二组:该文件拥有者所在组的其他成员对该文件的操作权限
第三组:其他用户组的成员对该文件的操作权限

# r表示读权限,w表示写权限,x表示执行权限

# 值是使用八进制进行计算的;例如:-rwxrwxrwx(前面的-代表这是一个普通文件)文件权限,计算过程如下:
rwx  rwx  rwx
111  111  111
 7   7     7
此时该文件的权限值为777

# 其他
777 代表
该文件拥有者对该文件拥有读写操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写操作的权限
其他用户组的成员对该文件也拥有读写操作权限


666代表
该文件拥有者对该文件拥有读写的权限但是没有操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写的权限但是没有操作的权限
其他用户组的成员对该文件也拥有读写权限但是没有操作的权限

# 所以
r(读)04,w(写)02,x(执行)01

3. ioutil.ReadFile打开

  • 其实调用的是 os.ReadFile()方法,os.ReadFile()是对os.open()的封装
  • ioutil.ReadFile() 和 os.ReadFile()都会打开文件并将文件内容一次性全读到内存中,在处理大文件时不友好
  • 无需手动关闭文件,在其函数内部已经封装了关闭文件的方法
函数定义:

func ReadFile(filename string) ([]byte, error) {   // 本质是调用了 OpenFile(name, O_RDONLY, 0)
	return os.ReadFile(filename)
}

- 其实是对 os.ReadFile()函数的封装,os.ReadFile()内部做了对文件的关闭,所以ioutil.ReadFile()不需要手动defer去关闭文件

二、文件读取

  • Go中文件的读取共有如下几种方式

    • 使用File类型自带的Read()方法,前提是要先打开文件。可以使用os.Open()os.OpenFile()打开文件(os.Open()本质还是调用了os.OpenFile()

    • 使用bufio库的Read方法,使用bufio.NewReader(文件对象)但需要先打开文件

    • 使用io/ioutil库的ReadAll(),使用ioutil.ReadFile(文件路径)ioutil.ReadFile(文件路径)本质还是调用了os.OpenFile()

1. File.Read普通读取文件

  • 类似python中的文件对象.read(n) 方法,n为读取的字节数
  • 可以和for循环组合使用,读取出文件的全部内容
  • 属于分块读取
函数的定义: 
func (f *File) Read(b []byte) (n int, err error)

- 它接收一个字节切片,返回读取的字节数和可能的具体错误,读到文件末尾时会返回0和io.EOF

(1)简单读文件

读取文件的简单实例:
func main() {
	file,err:=os.OpenFile("./xx.txt",os.O_RDONLY,0) // 打开文件
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	fmt.Println(file) // 文件句柄

	var buf []byte=make([]byte,1024)
	//var buf []byte=make([]byte,8) // 假设文件就9个字节,使用8字节的切片读是读不全的
	n,err:=file.Read(buf)
	if err == io.EOF {
		fmt.Println("文件读取完毕")
		return
	}
	if err != nil {
		fmt.Println("读取文件出错",err)
		return
	}
    fmt.Printf("读了%d字节个数据,数据内容为:%s",n,string(buf[:n]))  // string() 函数可以将字节切片直接转换成字符串
}

(2)循环读取文件

循环读取出文件的全部内容(上面如果buf大小太小,文件可能读不完,所以我们要循环读出所有内容)
func main() {
	file,err:=os.OpenFile("./xx.txt",os.O_RDONLY,0) // 打开文件
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	fmt.Println(file) // 文件句柄

	var buf []byte=make([]byte,5) // 存放每次读出的字节
	var content []byte // 存放所有读出来的字节
    
    // for{} 无限循环,类似python的while True
	for  {
		n,err:=file.Read(buf)
		if err == io.EOF {
			fmt.Println("文件读取完毕")
			break
		}
		if err != nil {
			fmt.Println("读取文件出错",err)
			break
		}
		content=append(content,buf[:n]...)  // 把每次读出的数据,追加到content中
	}
	fmt.Printf("数据内容为:%s",string(content))
}

2. bufio读取文件

  • bufio是在上面的普通读文件中的File 类型的基础上封装了一层API,支持更多的功能
  • 要先手动打开文件,再进行文件的读取,最后再手动关闭文件
  • 共有两种读取方式
    • 分块读取 bufio.NewReader()
    • 按行读取,有3个实现方法:reader.ReadLine()、reader.ReadString('\n')、bufio.NewScanner(file)

(1)分块读取

func main() {
	file,err:=os.Open("./xx.txt")  // 打开文件
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close()  // 关闭文件
	var buf =make([]byte,5)  // 定义好每次读取的块大小,其实就是一个字节切片
	reader:=bufio.NewReader(file)
	for  {
		n,err:=reader.Read(buf)
		if err == io.EOF {
			fmt.Println("读完毕")
			break
		}
		if err!=nil {
			fmt.Println("读取出错:",err)
			return
		}
		fmt.Println(string(buf[:n]))
	}
}

(2)按行读取

i. reader.ReadLine()

func main() {
	file,err:=os.Open("./xx.txt")
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	reader:=bufio.NewReader(file)
	for  {
		line,_,err:=reader.ReadLine()
		if err == io.EOF {
			break
		}
		fmt.Println(string(line))
	}
}

ii. reader.ReadString('\n')

func main() {
	file,err:=os.Open("./xx.txt")
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	reader:=bufio.NewReader(file)
	for  {
		line,err:=reader.ReadString('\n') // 按 \n 这个字符分隔读
		if err == io.EOF {
			if len(line)>0{
				fmt.Println(line)
			}
			break
		}
		fmt.Println(string(line))
	}
}

iii. bufio.NewScanner()

func main() {
	file,err:=os.Open("./xx.txt")
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close() // 关闭文件
	s := bufio.NewScanner(file) // 创建一个NewScanner
	for s.Scan() { //返回true表示没读完
		fmt.Println(s.Text()) // 把读出的内容,转成字符串
	}
}

3. 一次性全部读取

  • 共两种方式

    • ioutil.ReadFile() 和 os.ReadFile() 可以打开并读取文件全部内容到内存,然后自动关闭文件

    • 先手动打开文件,然后使用io.Reader读取文件类型的全部内容,再手动关闭文件

1. ioutil.ReadFile() 和 os.ReadFile()

func main() {
	content,err:=ioutil.ReadFile("./xx.txt")
    // content,err:=os.ReadFile("./xx.txt") 
	if err!=nil {
		fmt.Println("读取文件出错:",err)
	}
	fmt.Println("文件内容为:",string(content))
}

2. io.Reader

io/ioutil库的 ReadAll函数定义:
func ReadAll(r io.Reader) ([]byte, error) {
	return io.ReadAll(r)
}

- 其实是调用了io.ReadAll()

func main() {
	file,err:=os.Open("./xx.txt")  // 1.手动打开文件
	if err != nil {
		fmt.Println("打开文件出错:",err)
	}
	defer file.Close()  // 2.手动关闭文件
	content,err:=ioutil.ReadAll(file)  // 3.传入io.Reader
	if err!=nil {
		fmt.Println("读取文件出错:",err)
	}
	fmt.Println("文件内容为:",string(content))

}

二、文件写入

  • 想要写入文件,分为两种情况
    • 文件已存在,打开文件后向文件中进行覆盖写、追加写
    • 文件不存在,创建文件再进行写入
  • 使用 os.OpenFile() 和上面我们提到的打开文件时的 flag 参数,指定模式来打开文件,之后再结合文件写入相关的函数进行文件的写入

1. 文件写入时的打开文件

使用os库中的两个函数

func OpenFile(name string, flag int, perm FileMode) (*File, error) {}  // 根据后面的flag参数

func Create(name string) (*File, error) {}  

// Create函数:文件不存在,创建并打开文件,文件存在则打开文件进行读取和清空写,本质还是OpenFile--》return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)

2. File.Write(将字节写入文件)

func main() {
	file,err:=os.Create("xxx.txt")  // 创建并打开文件
	if err != nil {
		fmt.Println("打开文件错误:",err)
		return
	}
	defer file.Close()
	//d2 := []byte{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100}
	//d2 := []byte("hello world")
	d2 := []byte{'h','e','l','l','o'}
	n,err:=file.Write(d2)
	if err != nil {
		fmt.Println("写入文件出错:",err)
	}
	fmt.Printf("%d字节写入文件成功",n)
}

func main() {
	file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("open file failed, err:", err)
		return
	}
	defer file.Close()
	str := "hello 沙河"
	file.Write([]byte(str))       // 写入字节切片数据
	file.WriteString("hello 小王子") //直接写入字符串数据
}

// xxx.txt 内容:hello 沙河hello 沙河hello 小王子

3. File.WriteString(将字符串写入文件)

  • 返回写入的字节数和错误
func main() {
	file,err:=os.Create("xxx.txt")  // 创建并打开文件
	if err != nil {
		fmt.Println("打开文件错误:",err)
		return
	}
	defer file.Close()
	s:="hello world"
	n,err:=file.WriteString(s)
	if err != nil {
		fmt.Println("写入文件出错:",err)
	}
	fmt.Printf("%d字节写入文件成功",n)
}

4. bufio.NewWriter(推荐)

func main() {
	file, err := os.OpenFile(".\\hsw_test\\xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)  // 先打开文件
	if err != nil {
		fmt.Println("open file failed, err:", err)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString("hello沙河\n")  // 将数据先写入缓存
	}
	writer.Flush()  // 将缓存中的内容写入文件
}

5. ioutil.WriteFile

- ioutil.WriteFile 函数为清空写

- ioutil.WriteFile调用的是 os.WriteFile ,而 os.WriteFile 调用的是 OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm) 和 File.Write

- 写完自动关闭文件 

- 示例
func main() {
	err:=ioutil.WriteFile("xxx.txt",[]byte("xxx is nb"),0666)
	if err != nil {
		fmt.Println("写入文件出错",err)
		return
	}
}

三、其他扩展

1. 按行写

func main() {
	file, err := os.OpenFile(".\\hsw_test\\xx1.txt", os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("打开文件出错:", err)
		return
	}
	defer file.Close()
	d := []string{"Welcome to the world of Go", "Go is a compiled language.", "It is easy to learn Go."}
	for _, v := range d {
		fmt.Fprintln(file, v)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
}

/*
xx1.txt:

Welcome to the world of Go
Go is a compiled language.
It is easy to learn Go.
*/

2. 追加写

// 只要打开文件:file,err:=os.OpenFile(".\\hsw_test\\xx1.txt",os.O_APPEND|os.O_WRONLY,0666)

func main() {
	file,err:=os.OpenFile(".\\hsw_test\\xx1.txt",os.O_APPEND|os.O_WRONLY,0666)
	if err != nil {
		fmt.Println("打开文件出错:",err)
		return
	}
	defer file.Close()
	d := []string{"Welcome to the world of Go", "Go is a compiled language.", "It is easy to learn Go."}
	for _, v := range d {
		fmt.Fprintln(file, v)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
}

3. 并发写

  • 当多个 goroutines 同时(并发)写文件时,我们会遇到竞争条件(race condition)。因此,当发生同步写的时候需要一个 channel 作为一致写入的条件
  • 原理类似python多进程中的数据交互时使用的队列queue,将各个生产者 goroutines 产生的数据放入 channel 中,再定义n个消费者 goroutines 从channel 中取数据,从而避免竞争条件
实现过程:
1. 创建一个 channel 用来读和写这个随机数。
2. 创建 100 个生产者 goroutine。每个 goroutine 将产生随机数并将随机数写入到 channel 里。
3. 创建一个消费者 goroutine 用来从 channel 读取随机数并将它写入文件。这样的话我们就只有一个 goroutinue 向文件中写数据,从而避免竞争条件。
4. 一旦完成则关闭文件

***************************分隔线***************************

1. 我们开始写产生随机数的 produce 函数

func produce(data chan int, wg *sync.WaitGroup) {
    n := rand.Intn(999)
    data <- n
    wg.Done()
}


// 分析:上面的方法产生随机数并且将 data 写入到 channel 中,之后通过调用 waitGroup 的 Done 方法来通知任务已经完成。

2. 再让我们看看将数据写到文件的函数

func consume(data chan int, done chan bool) {
    f, err := os.Create("concurrent")
    if err != nil {
        fmt.Println(err)
        return
    }
    for d := range data {
        _, err = fmt.Fprintln(f, d)
        if err != nil {
            fmt.Println(err)
            f.Close()
            done <- false
            return
        }
    }
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        done <- false
        return
    }
    done <- true
}

/*
分析: 这个 consume 的函数创建了一个名为 concurrent 的文件。然后从 channel 中读取随机数并且写到文件中。一旦读取完成并且将随机数写入文件后,通过往 done 这个 cahnnel 中写入 true 来通知任务已完成
*/

3. 下面我们写 main 函数,并完成这个程序。下面是我提供的完整程序

package main

import (
	"fmt"
	"math/rand"
	"os"
	"sync"
)

// 生产者,每个生产者随机产生一个999内的数字
func producer(data chan int, wg *sync.WaitGroup) {
	r := rand.Intn(999)
	data <- r
	wg.Done()
}

func consumer(data chan int, done chan bool) {
	file, err := os.Create("concurrent") // 打开一个文件
	if err != nil {
		fmt.Println(err)
		return
	}
	for d := range data {
		_, err := fmt.Fprintln(file, d)
		if err != nil {
			fmt.Println("写入出错")
			file.Close()  // 关闭文件
			done <- false // 成功标志中写入false
			return
		}
	}
	file.Close() // 在for循环外面关闭文件
	done <- true
}
func main() {

	var (
		data = make(chan int)
		done = make(chan bool)
		wg sync.WaitGroup
	)
	// 启动1000个协程生产随机数
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go producer(data,&wg)
	}
	// 开启消费者,往文件中写随机数
	go consumer(data,done)


	go func() { //在另一个协程中等待所有生产者完成,然后关闭数据信道,不能写在主协程中
		wg.Wait()
		close(data)
	}()
	//从done中取出数据
	res:=<-done
	if res{ // true表示顺利写完
		fmt.Println("顺利写完")
	}else {// false 表示写出问题
		fmt.Println("写出问题")
	}

}


/*
分析:
main 函数创建写入和读取数据的 channel,创建 done 这个 channel,此 channel 用于消费者 goroutinue 完成任务之后通知 main 函数。创建 Waitgroup 的实例 wg,用于等待所有生产随机数的 goroutine 完成任务。

使用 for 循环创建 100 个 goroutines。调用 waitgroup 的 wait() 方法等待所有的 goroutines 完成随机数的生成。然后关闭 channel。当 channel 关闭时,消费者 consume goroutine 已经将所有的随机数写入文件, 将 true 写入 done 这个 channel 中,这个时候 main 函数解除阻塞并且打印 File written successfully
*/

标签:文件,err,fmt,文本处理,file,Println,os
From: https://www.cnblogs.com/Mcoming/p/18055996

相关文章

  • Linux文本处理三剑客---grep
    1.什么是grep和egrep示例grep[options]pattern[file...]其中,pattern表示要搜索的模式,可以是简单的文本字符串,也可以是正则表达式。file指定要在其中进行搜索的文件,如果省略file,则默认从标准输入中读取数据。egrep其实就是grep的一个变种,它支持更多的正则表达式语法。egre......
  • 轻松掌握 Linux 文本处理三剑客:grep、awk 和 sed 实战演练
     Shell脚本语言编程有哪些优势呢?Shell脚本语言的优势在于能够以最轻量级最快捷的速度处理Linux操作系统偏底层的业务。比如软件的自动化安装、更新版本,监控报警,日志分析等。虽然其他高级编程语言如PHP、Python、Ruby等语言也能做到,但是效率和开发成本上会大打折扣,所谓“......
  • 正则表达式与正则可视化工具:解密文本处理的利器
    引言在计算机科学和软件开发领域,正则表达式是一种强大而灵活的文本处理工具。然而,对于初学者来说,正则表达式的语法和规则可能会显得晦涩难懂。为了帮助初学者更好地理解和学习正则表达式,正则可视化工具应运而生。本文将介绍正则表达式的基本概念、语法和应用,并介绍如何利用正......
  • RHCE第三周 文本处理工具
    一:文本查看工具cat,tac,more,less,head,tail,wc这些命令1:cat和tac命令cat这个命令就是从头到尾的显示完整的文件内容,tac就是从尾到头的显示文件内容选项:-n:就是可以显示行数和文件内容-A:这个就是显示文件的不同点,可以看得到空格,这个非常的重要,就是在后面写shell脚本或者剧本的......
  • 【AI 实战】Text Processing and Word Embedding 文本处理以及词嵌入原理和代码实例讲
    文章目录【AI实战】TextProcessingandWordEmbedding文本处理以及词嵌入原理和代码实例讲解TexttoSequenceStep1:TokenizationStep2:BuildDictionaryStep3:One-HotEncodingStep4:AlignSequencesTextProcessinginKerasWordEmbedding:WordtoVectorHowtom......
  • 大数据分析与可视化 之 实验08 Pandas字符串和文本处理
    实验08Pandas字符串和文本处理实验学时:2学时实验类型:验证实验要求:必修一、实验目的学会正确使用常见的字符串函数如:len()、find()、strip()、replace()、contains()函数。解决实际数据中的字符串和文本处理问题。二、实验要求使用常见的字符串函数(如:len()、find()、s......
  • 八、文本处理工具
    文本提取工具(查看文本)文本分析工具文本操作工具1、文本提取工具cat、more、less查看文本内容cat:打印一个或多个文件到标准输出1#合并文件2[root@exampletmp]#catfile1.txtfile2.txt>file3.txt34#查看文件行号5[root@exampletmp]#cat-n/tmp......
  • 正则表达式与文本处理三剑客
    正则表达式使用方法通常为字符+次数+位置锚定1、字符匹配. 匹配任意单个字符,可以是一个汉字[] 匹配指定范围内的任意单个字符,示例:[wang][^] 匹配指定范围外的任意单个字符,示例:[^wang][0-9][a-z][a-zA-Z][:alnum:] 字母和数字[:alpha:] 代表任何英文大小写字......
  • shell变量类型--read--if语句正侧表达式(扩展)文本处理器、awk命令
    变量:是容器,值是可变的,变化的。作用就是增强脚本的灵活性。各种shell环境中都使用了“变量”的概念。shell变量用来存放系统和用户需要使用的特定参数(值),而且这些参数可以根据用户的设定或系统环境的变化而相应变化。通过使用变量,shell程序能够提供更加灵活的功能,适应性更强。变量(数......
  • shell变量类型--read--if语句正侧表达式(扩展)文本处理器、awk命令
    变量:是容器,值是可变的,变化的。作用就是增强脚本的灵活性。各种shell环境中都使用了“变量”的概念。shell变量用来存放系统和用户需要使用的特定参数(值),而且这些参数可以根据用户的设定或系统环境的变化而相应变化。通过使用变量,shell程序能够提供更加灵活的功能,适应性更强。变量(数......