linux文件编程
linux文件编程
操作系统提供了一系列的API,如linux系统:
打开——open
读写——read/write
光标定位——lseek
关闭——close
open
需要包含三个头文件
第一个参数是文件的路径,第二个flag参数是权限
Flags:
O_RDONLY只读打开 O_WRONLY只写打开 O_RDWR可读可写打开
附带权限后,打开的文件就只能按照这种权限来操作
上面三个只能选一个,下面的是可以选择的:
O_CREAT若文件不存在则创建它,使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限
O_EXCL如果同时指定了OCREAT,而文件已经存在,则出错
O_APPEND每次写时都加到文件的末端
O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0
mode
描述:给定一个文件路径,open函数返回一个文件描述符,一个小的非负整数。下一次系统调用队列(读写光标移动)等操作都要通过open的返回值——这个文件描述符 来操作
文件描述符
比如一个进程需要操作一个文件,那么对文件进行读写光标移动等操作,传递的参数就是open返回的文件描述符,这样系统才知道要对哪一个文件进行操作。
系统在打开文件的时候都会在内核里建立一个结构体来管理这些文件,描述符就会指向内核里的结构体,起到一个索引的作用。
比如有一个文件file,要编写一个temp1.c
在编写的时候可以输入 man 2 open 查看open需要的头文件
有这个file1文件存在,返回值是3
若删除file1
则返回值是-1
以上就是文件描述符
open函数的flag参数
O_CREAT
其中一个flag参数是O_CREAT若文件不存在则创建它,使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限
那么就可以在没有文件的情况下创建一个文件
这里的O_CREAT需要在三个一定存在的参数后面(O_RDONLY只读打开 O_WRONLY只写打开 O_RDWR可读可写打开)
只有打开了才能进行进一步操作
0600是权限参数
-表示普通文件
rwx代表当前用户对则个文件的权限
r可读 w可写 x可执行
可读是4 可写是2 可执行是1
0600
6 = 4 + 2,所以0600是可读可写
O_EXCL
最开始的读取方式的三个函数是互斥的关系,他们三个中只能存在一个
O_RDONLY只读打开 O_WRONLY只写打开 O_RDWR可读可写打开
O_EXCL表示如果使用O_CREAT,并且文件以存在,则出错
O_APPEND
如果在已存在的文件中输入一段数据
再使用write函数写入数据
再打开file1
会发现原先写入的数据被覆盖,且光标在第一位
原因是:以存在的文件,光标默认是在第一位,所以对其操作,写入数据的时候也是从第一位开始写入,于是就造成了覆盖
于是我们就需要使用O_APPEND,从结尾开始写入
O_TRUNC
O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0
原来文件中的内容如下
修改文件的内容和读取方式后编译运行
发现文件中的内容替换为test
说明不管有多少内容,用O_TRUNC的方式打开,都可以使内容全部消失,并替换成write函数所输入的字符串
写入文件
wirte函数
需要包含#include<unistd.h>
第一个参数是open函数返回的文件描述符
第二个参数是一个无类型的指针,是一个缓冲区
第三个参数是要写入的文件的大小
write函数作用就是,用无类型指针buf指向内存里的数据,写count字节到文件描述符指向的文件中
可以看到我们只写入了8个字节,因为sizeof指针,只有8个字节。因为在linux环境下指针的内存是8个字节
修改代码如下
执行后
读取函数
读取fd文件描述符所指向的文件,读取count个字节存放到无类型指针buf中
如果读取成功,读取了多少个字节就返回多少个字节,如果读到文件的尾巴,啥都没读到,就返回一个0,如果读取错误,就返回-1
可以看到,read函数的返回值是0,通过man手册发现,如果光标在文件的最后,那么什么也读取不到,最后的返回值就是0
既然光标文件的最后我们没有办法读取,那么我们就需要把光标移动到文件的最前面再重新打开,或者重新打开一次
我们先用重新打开文件的方式来操作
发现结果一致
如果读取的字节设置为100
而读取的结果仍然是16
说明,read函数的返回值为实际读取的字节数
write还能写入很多其他的数据,在write的原型中,第二个参数是无类型的指针,那么这个指针也可以是其他数据的地址
虽然人眼看着是乱码,但是机器知道这是一个整形数,已经写入成功
其他的数据类型同理,例如结构体,比如 struct Test data1;只要把data1的地址传给write就可以完成写入,链表也是一样,只要传首地址并且遍历
光标移动
lseek
lseek(int fd, off_t offset, int whence);
作用,将文件读写指针相对wence移动offset个字节
第一个参数是文件描述符
第二个参数是偏移值
第三个是位置
whence有三个位置,
SEEK_SET指向文件的头 SEEK_CUR光标当前位置 SEEK_END光标的结尾
将光标移动到文件的头部位置,并使偏移值为0,就可以达到移动光标到文件头的目的
↑这样的写法也是可以的↑
可以看到结果正确
我们通过查看man手册可以看到,lseek函数的返回值是偏移值,那么我们可以利用这个偏移值读取文件大小
返回值就是光标一共偏移的次数,这样就能读取文件大小
已存在的文件,光标就在第一个,所以将光标移动到结尾,就能得到文件大小
返回值是相对光标所在位置便宜的次数
创建文件
creat函数
mode:创建模式
常见的创建模式:
宏表示 数字
S_IRUSR 4 可读
S_IWUSR 2 可写
S_ISUSR 1 可执行
S_IRWXU 7 可读可写可执行
1、 对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX shell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。STDIN_FILE0、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0、1、2这几个数。
0 1 2是linux中默认存在的文件描述符
0是标准输入 1标准输出 2标准错误
标准输入输出在实际程序编写中用的不是很多,我们一般把程序运行错误打印到标准错误上去
2、文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回
给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。
3、文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。
open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。
对上述1中的标准输入和标准输出的应用
键盘输入hello
输出hello
文件平时存放在设备中的文件系统文件中,这种叫静态文件
用open或write函数对静态文件进行修改,系统内核在进程中就会建立一个结构体(包含fd,信息节点,buf等内容),buf存放文件的内容,内核申请一段空间存放这个buf,这个buf就是动态文件,所有read和write都是对动态文件进行操作
调用close函数时,就会把这个缓存区中的所有数据写道(更新)磁盘中
拷贝 cp 的实现
关于系统中的argc 和argv
这样就能实现类似cp demo1.c demo2.c
这样就把file1复制到file2
且内容完全一致
以上程序开辟了1024byte,有可能造成内存浪费或内存不够的情况
优化如下
此程序还存在的一个问题就是,如果复制的目标文件已经存在,并且内容很多,若复制过去的源文件内容比目标文件少,则造成的情况就是:源文件覆盖目标文件相同的大小,但目标文件多出来的大小仍会存在。
这时就需要用到
O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0
修改文件的内容
之前做的小项目能达到全部读取或全部写入
这个项目就是找到要修改的内容给到buf,再在文件中修改
查找用到的函数就是 strstr
strstt(目标字符串,要找的字符串);
返回值为要找的字符串第一次出现的位置
原来的文档内容
可以看到他的内容发生变化
并没有覆盖原来的内容,那么程序需要修改
再将光标移动到开始的位置
C中fopen、fread和linux中open、read
谭浩强书中写的对文件的操作函数是fopen fread
和linux中的open和read是不太一样的
https://www.cnblogs.com/NickyYe/p/5497659.html
fopen是c的标准库函数,open是基于linux系统的函数
后面的进程用到管道,管道就必须要用open进行开发
比如我们做驱动的时候,驱动是基于linux内核的,所以会有一定的移植性的限制,这个时候就要用到open,open是c的标准库函数,有良好的移值性
fopen是缓冲文件系统,文件直接通过内存开辟的缓冲区进行处理,然后存到外存,效率超高
open是通过内核处理文件,会不断在用户态和内核态来回切换,效率较慢
但是现阶段的计算机硬件已经非常好了,所以差别不是很大
fopen的使用
fopen是c标准库函数,所以只要包含stdio.h
返回的是一个文件流,不是一个文件描述符,这个文件流是一个指针
在fwrite和fread函数中,第二第三个函数表示对char所占字节数操作strlen(str)次,也可以写成对char所占字节数*strlen(str)操作一次
fwrite是把字符串中的数据写到文件流指针所指文件
fread是把文件流指针内容读到readBuf中
fseek和lseek类似
需要注意的是,fwrite要和fread等函数一起配套使用
查看文件,发现已写入
关于fread和fwrite的返回值
当修改第三个参数
写100次
写一次读100次
原因:只写了一次,总数只有一个,所以只能读一个
总结:返回值取决于第三个参数
fputc、feof、fgetc
fputc
可见已经插入
这个函数可以插入字符串,只要一个一个字符插就可以了‘
在用循环输出时要注意,指针移动会导致字符串的长度改变,所以需要在循环前就获得字符串的长度
feof()
fgetc
这两个一般一起用,在读取的时候用fgetc,读完的时候用feof来判断是否到了文件的末尾
feof在到达文件的末尾的时候返回一个非零值,否则返回0
成功输出
注意的是,feof在没有到达末尾的时候返回的是0,到达后返回非0