打开函数:open
读写函数:write/read
光标定位:lseek
关闭:close
man 2 open:在man手册第二页查看open函数
Linux下 0是标准输入,1是标准输出,2是标准错误。
一、打开文件
(1) int open(const char *pathname, int flags):
const char *pathname:是一个字符串,表示的是要打开的文件地址;
flags:包含以下标志位
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
O_CREAT: 若⽂件不存在,则创建它,需要使⽤mode选项。来指明新⽂件的访问权限。
open有返回值,返回3表示打开成功,返回-1表示打开失败,定义fd为返回值,也用于read函数里面的函数索引
(2) int open(const char * pathname, int flags, mode_t mode);
mode_t mode:代表文件权限
可读:r,数字4代表
可写:w,数字2代表
可执行:x,数字1代表
二、写文件
(3)ssize_t write(int fd, const void *buf, size_t count);给fd所指向的文件写数据
fd:文件标志返回符
buf:要往fd所指向的文件写入的东西
count:写入文件的大小
写入成功函数返回往文件写入的字节数
三、关闭文件
(4)close(fd):关闭fd所指向的文件
四、读文件
ssize_t read(int fd, void *buf, size_t count);
从fd指向的文件中读取coubt个字节放入buf中
读取成功返回读到的文件大小
!!!读写文件之前必须先打开文件
五、创建文件
open(const char *pathname, int flags);
pathname:在该路径下创建文件
flags:该文件的权限有:S_IRURE:可读
S_IWURE:可写
S_IXURE:可执行
S_IRWXU:该文件可读可写可执行
#include#include #include int main() { int fd; fd = creat("/home/CLC/file3",S_IRWXU); return 0; }
编译运行程序后我们可以在该目录下看见创建了一个名为file3的文件
六、文件偏移
off_t lseek(int fd, off_t offset, int whence);
fd:对fd所指向的文件进行光标偏移
offset:往后偏移这么多字节
count:从哪里偏移;SEEK_SET :光标移到文件头开始偏移
SEEK_CUR:从光标当前位置开始偏移
SEEK_END:从文件末尾开始偏移
lseek返回值是从文件头到偏移到当前位置所偏移的值(反应的是文件偏移量)。
巧妙计算文件大小 #include#include #include #include #include int main() { int fd; fd = open("./file",O_RDWR); int file_size = lseek(fd,0,SEEK_END); printf("The file size is %d byten",file_size); return 0; }
#include#include #include #include #include #include #include int main() { int fd; char* buf = "ChenLiChen Handsome!"; fd = open("./file",O_RDWR); if(fd < 0) { fd = open("./file",O_RDWR|O_CREAT,0600); if(fd > 0) { printf("creat file successn"); } } int n_write = write(fd,buf,strlen(buf));//重新打开写入文件 //close(fd); //fd = open("./file",O_RDWR);//读完文件以后光标已经移到文件末尾,(1)重新打开(2)移动光标 lseek(fd,0,SEEK_SET); char* read_buf; read_buf = (char*)malloc(sizeof(char)*n_write); int n_read = read(fd,read_buf,n_write); close(fd); printf("read %d byte contest:%sn",n_read,read_buf); }
注意:!!!O_RDWD与O_APPEND在对文件写入操作时的区别的区别:
#include#include #include #include #include int main() { int fd; char* buf = "ChenLiChen Handsome!"; fd = open("./file",O_RDWR); write(fd,buf,strlen(buf)); close(fd); return 0; }
位对file文件写入数据时,file文件里面内容是:
对file文件写入数据时,file文件里面内容是:
从这里我们看出,直接对文件写入时,默认是从文件头开始写入数据,会覆盖掉原来的信息。
#include#include #include #include #include int main() { int fd; char* buf = "ChenLiChen Handsome!"; fd = open("./file",O_RDWR|O_APPEND); write(fd,buf,strlen(buf)); close(fd); return 0; }
O_APPEND:每次写时都加入文件的末端,不覆盖原来的数据
O+TRUNC:每次写入数据时,把原先文件截短为0,并重新写入数据
!!!自己实现linux下cp指令:
我们先来了解一下main函数原型:
main 函数的标准原型应该是 int main(int argc, char *argv[]);
argc 是命令行参数的个数。而 argv 是一个指向指针的指针,为什么不是指针数组呢?因为前面讲过,函数原型中的[]表示指针而不表示数组,等价于 char **argv 。这个指针数组分别存放第1–n个命令行参数,参数不限。
#includeint main(int argc,char* argv[3]) { printf("total params is %dn",argc); printf("NO.1 Param is %sn",argv[0]); printf("NO.2 Param is %sn",argv[1]); printf("NO.3 Param is %sn",argv[2]); return 0; }
运行结果如图:
#include#include #include #include #include #include #include int main(int argc,char* argv[3]) { char* readbuf = {' '};//定义字符串初始化 int fdDes; int fdSrc; if(argc != 3) { printf("input errorn"); exit(-1); } fdSrc = open(argv[1],O_RDWR);//打开源文件 int size = lseek(fdSrc,0,SEEK_END);//求文件大小 lseek(fdSrc,0,SEEK_SET); readbuf = (char*)malloc(sizeof(char)*size+8); int n_read = read(fdSrc,readbuf,size); fdDes = open(argv[2],O_RDWR|O_CREAT|O_APPEND,0600); write(fdDes,readbuf,n_read); close(fdDes); close(fdSrc); return 0; }
这里我们的思路是:首先了解main函数的原型;我们先打开一个源文件,利用lseek巧妙读取文件大小,再将光标移向文件头,读取源文件里面的字节放入readbuf里面,接着我们打开目标文件(打开时清空目标文件内容,没有就创建,以可读可写方式打开),将readbuf里面的内容写入目标文件。
文件操作原理:
1.在linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
2.我们对文件进行操作时,一定要先打开文件,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,,一定要关闭文件,否则会造成文件损坏。
3.文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做到操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。
4.打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
5.为什么这么设计,不直接对块设备直接操作。块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。
文件应用:
!!!接下来我们来验证一下从键盘获取标准输入然后输出。从键盘获取标准输入存到readbuf里面,把读到的文件从标准键盘输出。
#include#include #include #include #include #include #include int main(int argc,char* argv[3]) { char readbuf[128]; read(0,readbuf,5);//从键盘读取5个字节 write(2,readbuf,strlen(readbuf));//输出到标准输出上 return 0; }
修改文件配置:
假如有这么一段文件:
SPEED=5;
LENG=3;
HIGH=8;
我们要修改LENG=3为LENG=5;注意写到文件里面的都是字符
我们了解一下strstr的用法:
char *strstr(const char *haystack, const char *needle);
在haystack字符串里面查找needle字符串。如果找到则返回到要找的字符串的开始位置处;查找失败返回空指针。
#include#include #include #include #include #include #include int main(int argc,char* argv[2]) { char* readbuf; int fdScr; if(argc != 2)命令行必须输入两个操作命令 { printf("input errorn"); exit(-1); } fdScr = open(argv[1],O_RDWR); int size = lseek(fdScr,0,SEEK_END);//lseek反应的是文件偏移量 lseek(fdScr,0,SEEK_SET);//光标重新移到文件头 readbuf = (char*)malloc(sizeof(char)*size);//而readbuf需要开辟(偏移量*字节数) read(fdScr,readbuf,size*sizeof(char));//把目标文件读到readbuf里面 char* p = strstr(readbuf,"LENG=");//在目标文件里面查找“LENG=”字符 if(p == NULL) { printf("Not Foundn"); exit(-1); } p = p + strlen("LENG=");//如果找到该字符串,则strstr函数返回该字符串开始的位置,我们让该字符串偏移到我们想要的位置 *p = '9';//修改我们需要的文件配置,以上均是把静态数据区的文件复制到动态数据区,修改以后的值是在动态数据区的readbuf,文件里面存的都是字符 lseek(fdScr,0,SEEK_SET);//光标移到文件头 write(fdScr,readbuf,strlen(readbuf));//把readbuf里面的值重新写到该文件从头覆盖 close(fdScr);//关闭该文件,并将数据同步更新到源文件 return 0; }
这是修改文件配置前文件的内容。
修改文件后文件的内容:
文件缓冲区不止会写字符,也会写数字,只会影响人类的判断,不会影响机器的判断。
#include#include #include #include #include #include #include struct data { int a; char b; }; //给一个指定的文件写入自己想要的数据,然后读出来 int main() { int fdSrc; char* readbuf; struct data test[2] = {{98,'w'},{89,'m'}}; struct data test1; fdSrc = open("./file",O_RDWR); write(fdSrc,&test,sizeof(struct data)*2);//给源文件写入后面这么多字节,写的内容是test2里面的内容 lseek(fdSrc,0,SEEK_SET);//写完数据光标重新移到文件头要读数据 readbuf = (char*)malloc(sizeof(struct data)*2); read(fdSrc,&test1,sizeof(struct data)*2); close(fdSrc); printf("%d %cn",test[0].a,test[0].b); printf("%d %cn",test[1].a,test[1].b); return 0; }
我们看看运行结果以及文件内容:
但是文件内部却是这样的,因为我们查看文件时,看到的都是字符,这里我们写入的是真实的数字,读取时也是%d格式打印的数字,影响了人类的判断,机器却能认识。
!!!C语言中文件的操作:
fopen与open的区别:
fopen具有很好的移植性,是C标准函数;open是unix下的系统调用函数,移植性有限。
fopen配套fwrite,fread一起使用;open配套write,read一起使用。不能互相使用。
FILE *fopen(const char *path, const char *mode);//返回的是文件标识符 fopen函数用的是标准C语言库,第一个参数是文件路径,第二个参数是文件权限。 r:以只读方式打开文件,该文件必须存在。 r+:以读/写方式打开文件,该文件必须存在。 rb+:以读/写方式打开一个二进制文件,只允许读/写数据。 rt+:以读/写方式打开一个文本文件,允许读和写。 w:打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 w+:打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 a:以附加的方式打开只写文件。若文件不存在,则会创建该文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF 符保留)。 a+:以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。 wb:以只写方式打开或新建一个二进制文件,只允许写数据。 wb+:以读/写方式打开或新建一个二进制文件,允许读和写。 wt+:以读/写方式打开或新建一个文本文件,允许读和写。 at+:以读/写方式打开一个文本文件,允许读或在文本末追加数据。 ab+:以读/写方式打开一个二进制文件,允许读或在文件末追加数据。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数一:要往文件写入的内容,是字符串格式
参数二:一次写入的字节数
参数三:写多少次
参数四:目标文件标识符
下面我们来写一个用fopen、fread、fwrite、fseek来给一个文件写入结构体。
#include#include struct data { int a; char b; }; int main() { struct data test2 = {1,'q'}; struct data test1; FILE* fp; fp = fopen("./file2","w+");//返回文件标识符 int n_write = fwrite(&test2,sizeof(struct data)*2,1,fp);//每次写多少数据 ,写多少次 fseek(fp,0,SEEK_SET);//光标移到文件头 int n_read = fread(&test1,sizeof(struct data)*2,1,fp); fclose(fp); printf("fwrite is return %dn",n_write); printf("fread is return %dn",n_read); printf("test1.a = %d test1.b = %cn",test1.a,test1.b); return 0; }
前面我们验证过unix下我们可以通过读写往文件写入一个结构体数组,这里使用C语言环境进行代码编写,却不可以了这个我们进行后续研究。
fwrite、fread均是在目标文件中进行读写,返回值是读写的次数,这里有两种读写的方式;
(1)可以读写一次,一次读写完全部的数据
(2)可以一次写入一个数据类型大小,读写n次
文件操作中其他API:
fputc(str,fp),往目标文件写入一个字符。
#include#include int main() { char a = 'A'; FILE* fp = fopen("./qaz","w+"); fputc(a,fp); return 0; }
往目标文件写入一个字符串:
#include#include int main() { char* str = "chenlichen shuai o !"; int i; FILE* fp = fopen("./qaz","w+"); int len = strlen(str); for(i=0;i fputc(*str,fp);//每次往目标文件写入一个字符 str++;//写完一个字符++ } return 0; }
feof(fp)//没到达文件末尾,返回值是0,到达文件末尾,返回值不为0
从文件每次读取一个字符打印出来,
#include#include int main() { FILE* fp = fopen("./qaz","r");//以只读方式打开文件 int i; char c; while(!feof(fp)) { c = fgetc(fp);//每次从文件里面读取一个字符存到c,读完以后自动后移 printf("%c",c); } fclose(fp); return 0; }



