目录
一、linux 文件操作原理
1、在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
2、强调一点:我们对文件进行操作时,一定要先打开文件,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,一定要关闭文件,否则会造成文件损坏。
3、文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。
4、打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
5、为什么这么设计,不直接对块设备直接操作。块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。
1.静态文件
存储在磁盘内的文件
2.动态文件
打开静态文件后,内核会产生一个结构体,fd,信息节点,buf 简单来说就是申请的缓冲区内存,最后调用close 可以把buf内的内容保存回文件。
用来给 read write进行操作的缓冲区 。
二、
1.实现cp功能
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, int **argv)
{
int buf[1024];
if (argc != 3)
{
printf("error: unexpected number of arguments");
exit(-1);
}
int fdSrc = open(argv[1], O_RDWR);
int fdDes = open(argv[2], O_RDWR | O_CREAT);
int Size = read(fdSrc, buf, sizeof(buf));
write(fdDes, buf, Size);
close(fdDes);
close(fdSrc);
}
这里二级指针会有点警告,不过这个是符合的。
这里实现cp argv[1] 的文件 到argv[2]中 如果argv[2]没有那就会新建一个 就是linux内的cp操作。
2.IO口写入操作
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, int **argv)
{
char *buf;
char *p = NULL;
int fd = open(argv[1], O_RDWR);
if (argc != 2)
{
printf("Error opening file\n");
exit(-1);
}
int Size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buf = (char *)malloc(sizeof(char) * Size);
int n_read = read(fd, buf, Size);
p = strstr(buf, "LENG=");
if (p == NULL)
{
printf("Error reading");
exit(-1);
}
p = p + strlen("LENG=");
*p = '5';
lseek(fd, 0, SEEK_SET);
write(fd, buf, n_read);
close(fd);
return 0;
}
修改文件中LENG= 后面的数字改为5
注意:lseek(fd, 0, SEEK_SET); 要在写入之前添加因为read读取后会把光标转移。
3.记录一个结构体
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct Test
{
int a;
char b;
};
int main()
{
struct Test data1[2] = {{100,'a'},{200,'b'}};
struct Test data2[2];
int write_fd = open("./filed",O_RDWR|O_CREAT);
int n_write = write(write_fd,&data1,sizeof(struct Test)*2);
lseek(write_fd,0,SEEK_SET);
int n_read = read(write_fd,&data2,sizeof(struct Test)*2);
printf("read %d %d\n",data2[0].a,data2[1].a);
close(write_fd);
return 0;
}
实际上就是缓冲区内传入一个结构体型的地址 这里用的是一个结构体数组
对文件中写入写入结构体 然后读取结构体
4.fopen与open
1.来源从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别:·open是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。·fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。PS:从来源来看,两者是有千丝万缕的联系的,毕竟C语言的库函数还是需要调用系统API实现的。
2.移植性这一点从上面的来源就可以推断出来,`fopen'是C标准函数,因此拥有良好的移植性;而`open'是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数CreateFile>。
3.适用范围·opem返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作普通正规文件(Regular File)。·fopen是用来操纵普通正规文件(Regular File)的。
4.文件IO层次如果从文件IO的角度来看,前者属于低级IO函数,后者属于高级I0函数。低级和高级的简单区分标准是:谁离系统内核更近。低级文件10运行在内核态,高级文件IO运行在用户态。
总的来说 搞linux要用open
拓展:
1.缓冲文件系统缓冲文件系统的特点是:在内存开辟一个"缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件得读入内存"缓冲区”,装满后再从内存"缓冲区"依此读出需要的数据。执行写文件的操作时,先将数据写入内存"缓冲区”,待内存"缓冲区"装满后再写入文件。由此可以看出,内存"缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区"越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件"缓冲区"的大小随机器而定。fopen,fclose,fread,fvrite,fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等。
2.非缓冲文件系统缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open,close,read,write,getc,getchar,putc,putchar等。一句话总结一下,就是open无缓冲,fopen有缓冲。前者与read,write等配合使用,后者与fread,fwrite等配合使用。使用fopen函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read,write);而使用open函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列的函数快;如果随机访问文件则相反。这样一总结梳理,相信大家对于两个函数及系列函数有了一个更全面清晰的认识,也应该知道在什么场合下使用什么样的函数更合适,效率更高。
fopen 返回的是文件流呀
fwrite()
函数用于向文件中写入二进制数据。它需要四个参数:
ptr
:指向要写入文件的数据的指针。size
:要写入每个元素的字节数。nmemb
:要写入的元素个数。stream
:指向目标文件的指针。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct data
{
int a;
char* c;
};
int main()
{
FILE *file;
struct data data1 = { 100 , "gmtx"};
struct data data2;
file = fopen("file.txt", "w+");
fwrite(&data1, sizeof(char), sizeof(struct data), file);
fseek(file, 0, SEEK_SET);
fread(&data2, sizeof(char), sizeof(struct data), file);
printf("%d %s\n",data2.a, data2.c);
fclose(file);
return 0;
}
4.fget fput fclose feof
直接上代码叭
#include <stdio.h>
#include <string.h>
int main(){
FILE *fp = fopen("file2.txt", "w+");
char* str = "gmtx yyds";
int len = strlen(str);
for (int i = 0; i < len; i++)
{
fputc(*str, fp);
str++;
}
fclose(fp);
return 0;
}
#include <stdio.h>
#include <string.h>
char c;
int main()
{
FILE *fp = fopen("file2.txt", "r");
if (fp == NULL)
{
printf("Error opening");
exit(-1);
}
while (!feof(fp)) // 到最后返回 1
{
c = fgetc(fp);
printf("%c", c);
}
fclose(fp);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct data
{
int a;
char* c;
};
int main()
{
FILE *file;
struct data data1 = { 100 , "gmtx"};
struct data data2;
file = fopen("file.txt", "w+");
fwrite(&data1, sizeof(char), sizeof(struct data), file);
fseek(file, 0, SEEK_SET);
fread(&data2, sizeof(char), sizeof(struct data), file);
printf("%d %s\n",data2.a, data2.c);
fclose(file);
return 0;
}
总结
对于linux学习,主要围绕open sleek IO写入 read write等api为重点 f家族不需要太多的关注。大家参照代码进行复习。