文件描述符
文件描述符fd是通过open打开某一文件后返回的非负整数。在Linux系统中默认存在的文件描述符有0------标准输入,1------标准输出,2------标准错误
c
#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 fd;
char readBuf[128];
int n_read = read(0,readBuf,5);
int n_write = write(1,readBuf,strlen(readBuf));
printf("\ndone!\n");
return 0;
}
上述代码的作用就是使用read基于标准输入通过键盘读取5个字符,之后使用write基于标准输出读取5个字符。
文件描述符只针对当前进程。
在linux中对文件操作需要注意的点
- 在linux中药操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
- 对文件操作时一定要先打开文件,打开成功之后次啊能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后一定要关闭文件,否则会造成文件损坏。
- 文件平时是存放在块设备(磁盘)中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构(结构体),记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存在(叫动态文件)
- 打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
- 为什么这么涉及,不直接对块设备进行操作?
块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。
实现linux cp命令的代码
cp src.c des.c
思路:
首先要对参数进行拆解
1.打开src.c
2.读src中的内容到buf
3.打开/创建des.c
4.将buf写入到des.c
5.close两个文件
参数拆解
因为我们需要打开源文件和目标文件,因此需要对cp src.c des.c进行参数拆解。代码如下:
c
#include <stdio.h>
int main(int argc,char** argv)
{
printf("total params:%d\n",argc);
printf("No.1 params:%s\n",argv[0]);
printf("No.2 params:%s\n",argv[1]);
printf("No.3 params:%s\n",argv[2]);
return 0;
}
此时需要补全main函数的参数argc和argv;
代码实现cp命令
c
#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,char** argv){
int fdSrc,fdDes;
char *readBuf=NULL;
if(argc != 3){ //参数个数不为3时直接报错退出
printf("params error\n");
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_TRUNC,0600); //打开目标文件,如果不存在就creat,如果已经存在了,就会根据O_TRUNC清空之前的数据。
int n_write = write(fdDes,readBuf,strlen(readBuf)); //写入数据
close(fdSrc); //关闭文件,不要忘记了!
close(fdDes);
return 0;
}
运行代码gcc test4.c -o mycp
./mycp test4.c new.c
就可以实现对test4.c进行复制的操作
文件编程 修改配置文件
思路:
1.找到需要修改那一行的位置a
2.a往后移动到我们需要修改的参数之前的位置b
3.修改b位置的参数
可以利用strstr函数来找到a位置
**char *strstr(const char haystack, const char needle);
RETURN VALUE
These functions return a pointer to the beginning of the substring, or NULL if the substring
is not found.返回子串的起始位置。
c
#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,char** argv){
int fdSrc;
char *readBuf=NULL;
if(argc != 2){
printf("params error\n");
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);
char *p = strstr(readBuf,"LENG=");
if(p == NULL){
printf("not found\n");
exit(-1);
}else{
printf("change success\n");
}
p=p+strlen("LENG=");
*p = '5'; //考虑如果我们写入的是整型数5,会是什么情况
lseek(fdSrc,0,SEEK_SET);
int n_write = write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
return 0;
}
原先的数据是:
SPEED=3
LENG=3
SCORE=9
LEVEL=5
修改后的数据为:
SPEED=3
LENG=5
SCORE=9
LEVEL=5
修改配置文件时,写入一个整数
c
#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 fd;
int data1 = 10;
int data2 = 0;
fd = open("./file1",O_RDWR);
int n_write = write(fd,&data1,sizeof(int));
lseek(fd,0,SEEK_SET); //使光标回到头
int n_read = read(fd,&data2,sizeof(int));
printf("read:%d\n",data2); //输出read:10
close(fd);
return 0;
}
写入一个结构体
c
#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 c;
};
int main(){
int fd;
struct Test data1 = {10,'a'};
struct Test data2;
fd = open("./file1",O_RDWR);
int n_write = write(fd,&data1,sizeof(struct Test));
lseek(fd,0,SEEK_SET);
int n_read = read(fd,&data2,sizeof(struct Test));
printf("read:%d %c\n",data2.a,data2.c);
close(fd);
return 0;
}
总结:
不要陷入思维定势!!
*ssize_t write(int fd, const void buf, size_t count);
*ssize_t read(int fd, void buf, size_t count);
write和read的第二个参数,不一定非得是一个字符数组,只要求是一个指针即可,指针的本质就是地址,因此传入一个地址也是可以的,就像上面的两块代码一样,传入&data1也是可以的!
了解fopen和open的区别
- 来源:****open 是UNIX系统调用函数(包括LINUX),返回的是文件描述符fd,它是文件在文件描述符表里的索引。fopen 是标准C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。
2.移植性 : fopen 是C标准函数,因此有良好的移植性;而open是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数'CreateFile'。 - 适用范围 :open 的适用范围比fopen大,fopen适用于操纵普通正规文件,而UNIX下的一切设备都是以文件的形式操作,如网络套接字、硬件设备等。
- 文件IO层次 :open 属于低级IO函数,fopen 属于高级IO函数。低级和高级的简单区分标准是:谁离内核更近。低级文件IO运行在内核态,高级文件IO运行在用户态。
5.缓冲 : fopen 都在缓冲区中进行操作,操作外存的次数少,执行速度快,效率高;open需要在用户态和内核态之间切换,效率相对较低