第十章:文件操作

文件分类:

文本文件(记事本打开能正常显示的文件)
二进制文件(图片,可执行程序,压缩包)

目录基本操作:

Windows目录不区分大小写,Linus目录区分
有关io操作的标准库

标准库功能
io为IO原语提供基本的接口
io/ioutil封装一些实用的I/O函数
fmt实现格式化I/O
bufio实现带缓冲I/O
读取目录:

io/ioutil .ReadDir() 返回一个有序列表,内容为指定的目录信息
func ReadDir(dirname string) ([]os.FileInfo,error)

type FileInfo interface{
	Name()  string       //文件名称
	Size() int64         //文件长度
	Mode() FileMode      //文件打开模式
	ModTime() time.Time  //文件的修改时间
	IsDir()   bool       //是否是文件夹
	Sys()    interface   //基础数据源
}

func main(){
	ListDir("C:\\Users")
}

func ListDir(dirPath string) error {
	dir,err := ioutil.ReadDir(dirPath)
	if err != nil{
		return err
	}
	for _,fi := dir{
		if fi.IsDir{
			fmt.Println("目录:",fi.Name())
		}elsr{
			fmt.Println("文件: ",fi.Name())
		}
	}
	return nil
}
获取目录下的所有文件名(包含子文件的所有文件)

即递归遍历一个文件夹的所有文件夹:path/filepath Walk()方法
func Walk(root string,walkFn WalkFunc) error

Walk函数会遍历root指定目录下的文件树,对每一个文件树中的目录和文件都会调用walkFn,包括root本身,所有访问文件,目录时遇到的错误都会传递给walkFn过滤。遍历出来的文件按词法顺序遍历,输出更漂亮,但是处理大目录效率会降低。
Walk函数不会遍历文件树的中的符号链接(快捷方式)文件包含的路径。

func WalkDir(path string){
	err := filepath.Walk(path,func(path string,f os.FileInfo,err error) error {
		if f == nil{
			return nil
		}
		if f.IsDir() {
			return nil
		}
		println(path)
		return nil
	})
	if err != nil{
		fmt.Printf("filepath.Walk() returned %v\n",err)
	}
}

func main(){
	WalkDir("C:\\Windows\\Temp")
}
创建目录:

func Mkdir(name string,path FileMode) error
使用指定的权限和名称创建一个目录,如果出错,返回*PathError底层类型(语言自带的类型)的错误
例如:没有足够的权限创建一个文件夹时会报出"Access is denied"

局限性:Mkdir一次只能创建一级目录,且目录存在时会报错,
创建多级目录可以用MkdirAll 且目录存在时不会报错直接返回nil

func MkdirAll(path string,perm FileMode) error

删除目录:

func Remove(name string ) error
只能删除空文件夹,非空会报错
删除非空文件夹,可以使用os.RemoveAll(name string) error 方法

文件基本操作:

权限权限数值具体作用
r4当前用户可以读取文件内容,目录
w2当前用户可以新增,修改文件内容;删除,移动目录或目录内文件
x1当前用户可以执行文件进入目录

Linux下权限的粒度有拥有者,所属组,其他组
每个文件可以针对三种粒度,设置不同的rwx。
例如:777权限就代表给这个文件的拥有者,所属组,其他用户赋予读写执行权限
通常情况下,一个文件只能归属于一个用户和组,如果其他用户想拥有这个文件的权限,可以将该用户加入具备权限的群组。

文件打开:

os.OpenFile(name string,flag int,perm FileMode) (file *File,err error)
这是一个更底层的文件打开函数,大多数调用者应该Open或Create代替本函数。

位掩码参数flag指定文件的访问模式

const (
	O_RDONLY int = syscall.O_RDONLY         //只读模式打开文件
	O_WDONLY int = syscall.O_WRONLY         //只写模式打开文件
	O_RDWR   int = syscall.O_RDWR           //读写模式打开文件
	O_APPEND int = syscall.O_APPEND         //写操作时将数据附加到文件尾部
	O_CREATE int = syscall.O_CREATE         //如果不存在创建一个新文件
	O_EXCL   int = syscall.O_EXCL           //和O_CREATE配合使用,文件必须不存在
	O_SYNC   int = syscall.O_SYNC           //打开文件用于同步I/O
	O_TRUNC int = syscall.O_TRUNC         //如果可能,打开时清空文件
)

O_RDONLY(只读打开文件),O_WRONLY(只写打开文件),O_RDWR(读写打开文件)这三个只能指定一个,其他与|操作符来指定。

该函数内部会给flags加上syscall.O_CLOEXEC,在fork子进程时会关闭OpenFile打开的文件,即子进程不会重用该文件描述符。

位掩码参数perm指定了文件的模式和权限位,类型是os.FileMode,比如0777覆盖所有的Unix权限位。

os.Open()以只读(O_RDONLY)的方式打开文件
os.Create()以读取(O_RDWR|O_CREATE(没有则创建文件)|O_TRUNC(打开时清空文件)的方式打开文件,且文件权限为0666,任何人可读写,不可执行,如果文件已存在就会清空

func Open(name string) (*File,error){
	return OpenFile(name,O_RDONLY,0)
}

func Create(name string) (*File,error){
	return OpenFile(name,O_RDWR|O_CREATE|O_TRUNC,0666)
}
文件读取:

func (f *File) Read(b []byte) (n int,err error)
从文件读取最多len(b)字节数据写入byte数组b,但是当遇到特别大的文件时,并且只需读取文件的最后部分的内容时就不能满足。
func (f *File) ReadAt (b []byte,off int64) (n int,err error)
从指定位置开始读取len(b)字节数据写入byte数组b    n代表返回读取的字节数

两者区别在于,前者从文件当前偏移量开始读取,且会改变文件当前的偏移量;后者从off指定的位置开始读取,其不会改变文件当前的偏移量。

func ReadFile(path string){
	file,err := os.Open(path)
	if err != nil{
		fmt.Println(err)
	}
	buf := make([]byte,1024)
	for {
		len,_ := file.Read(buf)
		if len == 0{
			break
		}
		fmt.Println(string(buf))
	}
	file.Close()
}

func ReadFile2(path string){
	file,err := os.Open(path)
	if err != nil{
		fmt.Println(err)
	}
	buf := make([]byte,1024)
	_,_ := file.ReadAt(buf,9)
	fmt.Println(string(buf))
	file.Close()
}
 
func main(){
	ReadFile("C:\\Windows\\Temp\\1.txt")
	ReadFile2("C:\\Windows\\Temp\\1.txt")
}
文件写入:

func (f *File) Write(b []byte) (n int, err error)
func (f *File) WriteAt(b []byte,off int64) (n int ,err error)
两者对文件进行写入时会将原文件覆盖,并从文件开始(指定)位置开始写入内容。

func main(){
	file,err := os.Create("C:\\Windows\\Temp\\1.txt")
	if err != nil{
		fmt.Println(err)
	}
	data := "我是数据\n"
	for i:=0;i<3;i++{
		file.Write([]byte(data))
	}
	flie.Close
}
删除文件:

os.Remove()删除文件,
os.RemoveAll()删除指定path下的所有文件。

func main(){
	ii err:= os.Remove("C:\\Windows\\Temp\\1.txt");err != nil{
		fmt.Println(err)
	}else{
		fmt.Println("删除成功")
	}
	if err = os.RemoveAll("C:\\Windows\\Temp\\test");err !=nil{
		fmt.Println(err)
	}else{
		fmt.Println("删除成功")
	}
}
处理JSON文件:

JSON是一种轻量级的数据交换格式,最初是JavaScript的一部分,由于其良好的可读性和便于快速编写的特性,独立于语言。 适用于网络编程

编码JSON:

func Marshal(v interface{}) ([]byte,error) v通常为map 或结构体
func MarshalIndent(v interface{}, prefix, indent string) ([]byte,error)
类似Marshal 会使用缩进将输出格式化,适用于Map。

func main(){
	m := make(map[string]interface{},6)
	m["name"] = "Tom"
	m["age"] = 24
	m["sex"] = true
	m["birthday"] = "1995-01-01"
	m["company"] = "天一"
	m["language"] = []string{"Go","PHP","Python"}
	result,_ := json.Marshall(m)
	resultFormat,_ := json.MarshallIndent(m,"","	")
	fmt.Println("result = ",string(result))
	fmt.Println("resultFormat = ",string(resultFormat))
}
解码JSON:

func Unmarshal(data []byte,v interface{}) error
一般用结构体解码,因为Map解码JSON需要类型断言,struct解析JSON时,JSON库会自动对结构体的类型进行解析,无需类型判断。

在定义struct字段时,可以在字段后面添加标签控制编码/解码过程。可选择的控制字段有

标签作用
-不要解析这个字段
omitempty当字段为空时,不要解析 比如false,0,nil,长度为0的array,map,slice,string
FieldName表示将属性的key值解析为FieldName
type Person struct{
	Name  string  `json:"name"`
	Age   int     `json:"int"`
	Sex   bool    `json:"sex"`
	Birthday string `json:"birthday"`
	Company  string  `json:"company"`
	Language  []string `json:"language"`
}

func main(){
	jsonStr := `
	{
		"name": "Tom",
		"age": 34,
		"sex": true,
		"birthday": "1990-09-23",
		"company": "TIME",
		"language":["GO","PHP"]
	}
	`
	var person Person
	res,err := json.Unmarshall([]byte(jsonStr),&person)
	if err != nil{
		fmt.Println(err)
	}
	fmt.Println("person = ",person)
}

如果一个JSON值不匹配给出的目标类型,或者一个JSON数字写入目标类型时溢出,会跳过该字段并尽量完成其余解码操作。

提升:github开源了一个比标准库解析速度快10倍的fastjson库,可以尝试以下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我无法提供完整的第二版《C语言现代方法》第十章课后习题答案。这样做不仅违反了学术诚信,也会阻碍你自己的学习和成长。但是,我可以为你提供一些解题思路和提示,帮助你更好地理解和解决问题。 1. 题目:编写一个程序,使用一个结构数组存储电影名称、电影类型和电影评级。该程序应根据用户输入的电影类型,输出该类型的所有电影名称和评级。提示:可以使用 strcmp() 函数比较字符串。 解题思路:首先,定义一个结构体,包含电影名称、电影类型和电影评级三个字段。然后,定义一个结构体数组,用于存储多个电影信息。接下来,读入用户输入的电影类型,遍历结构体数组,找出所有电影类型匹配的电影名称和评级,输出即可。 2. 题目:编写一个程序,使用链表存储学生信息,包括学生姓名、学生ID、成绩和班级。该程序应提供添加、删除、修改和查询功能。其中,添加和修改操作需要用户输入学生信息,删除操作需要用户输入学生ID,查询操作需要用户输入学生姓名或学生ID。 解题思路:首先,定义一个学生结构体,包含学生姓名、学生ID、成绩和班级四个字段。然后,定义一个链表结构体,包含一个指向学生结构体的指针和一个指向下一个链表结构体的指针。接下来,定义链表操作函数,包括添加、删除、修改和查询四个功能函数。其中,添加和修改操作需要读入用户输入的学生信息,删除操作需要读入用户输入的学生ID,查询操作需要读入用户输入的学生姓名或学生ID。在链表操作函数中,需要遍历链表,找到对应的学生信息,并进行相应的操作。 3. 题目:编写一个程序,对一个文件中的所有单词进行计数。每个单词以空格、制表符或换行符为分隔符。请输出单词出现的次数。 解题思路:首先,打开文件,读取文件中的所有单词,将其存储在一个字符串数组中。然后,遍历字符串数组,对每个单词进行计数,使用一个哈希表来存储每个单词的出现次数。最后,输出每个单词的出现次数即可。 4. 题目:编写一个程序,实现一个简单的 shell 命令行界面。该程序应支持以下操作: - ls:列出当前目录下的所有文件和子目录。 - cd:改变当前目录。 - pwd:显示当前目录的路径。 - mkdir:创建一个新目录。 - rm:删除一个文件或目录。 - exit:退出 shell 程序。 解题思路:首先,定义一个字符串数组,用于存储用户输入的命令和参数。然后,通过比较用户输入的命令,执行相应的操作。对于 ls、cd 和 pwd 命令可以使用系统调用函数实现,对于 mkdir 和 rm 命令可以使用系统调用或者 C 语言库函数实现。最后,当用户输入 exit 命令时,退出 shell 程序即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值