一、Linux——文件
1.1 打开/创建文件
函数的返回值称为文件描述符,一般用fd表示(失败返回-1)。
参数说明:
pathname 要打开的文件名(含路径,缺省为当前路径)
flags O_RDONLY 只读打开 O_WRONLY 只写打开 O_RDWR 可读可写打开 (三选一)
其它flags O_EXCL 如果同时指定了O_CREAT且文件已经存在 则打开失败
O_APPEND 打开文件后每次写入都加到文件尾端
O_TRUNC 如果这个文件中本来是有内容的,而且为只读或者只写成功打开,则将其长度 截断为0 打开文件后,把原本的内容全部覆盖掉
Mode 一定是flags中使用了O_CREAT标志,mode记录待创建文件的权限
可读权限 r 4 宏表示:S_IRUSR
可写 w 2 宏表示:S_IWUSR
可执行 x 1 宏表示:S_IXUSR
可读、写、执行 7 宏表示:S_IRWXU
例如:0700 就代表可读可写 主要关注第二个数字
Demo:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//上面三个头文件是使用open函数所必须的
#include <stdio.h>
int main()
{
int fd;
fd=open("./filetest",O_RDWR|O_CREAT,0700);
printf("fd=%d",fd);
return 0;
}
1.2 文件写入操作
参数说明:
fd 要写入的文件
buf 待写入的的数据
count 要写入的字节数
返回值 成功时返回写入的字节数,失败直接返回-1
Demo:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//上面三个头文件是使用open函数所必须的
#include <unistd.h>//使用close和write必须的头文件
#include <stdio.h>
#include <string.h>
int main()
{
int fd;
char *buf="hello world";
fd=open("./filetest",O_RDWR|O_CREAT,0700);
if(fd==-1)
{
printf("open failed\n");
}
else
write(fd,buf,strlen(buf));//这里用sizeof的话求得是指针变量的长度而不是字符串的长度
close(fd);//务必记得要关闭文件
return 0;
}
1.3 文件读取操作
参数说明:
fd 要读取的文件
buf 读取后存放的地址
count 要读取的字节数
返回值 成功时返回读取的字节数,失败直接返回-1
Demo:
#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 *buf = "chenLichen hen shuai!";
fd = open("./file1",O_RDWR);
if(fd == -1){
printf("open file1 failed\n");
fd = open("./file1",O_RDWR|O_CREAT,0600);
if(fd > 0){
printf("create file1 success!\n");
}
}
printf("open susceess : fd = %d\n",fd);
// ssize_t write(int fd, const void *buf, size_t count);
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to file\n",n_write);
}
char *readBuf;
readBuf = (char *)malloc(sizeof(char)*n_write + 1);
// ssize_t read(int fd, void *buf, size_t count);
int n_read = read(fd, readBuf, n_write);
printf("read %d ,context:%s\n",n_read,readBuf);//光标写入完就一直在末尾 什么也读不到
close(fd);
return 0;
}
注意文件中光标的位置。
1.4 光标的移动
函数表述:将文件读写指针相对whence移动offset个字节
参数描述:
whence: SEEK_SET 文件头 SEEK_END 文件尾 SEEK_CUR 光标当前位置
返回值为相对于文件头偏移的字节数,可以用filesize=lseek(fd,0,SEEK_END)来计算文件的字节数。
Demo:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int fd;
char *buf ="Refuel.CONG";
fd= open("./file",O_RDWR);
printf("fd=%d\n",fd);
if(fd == -1){
printf("open file failed\n");
fd = open("./file", O_RDWR | O_CREAT | ,0600);
if(fd>0){
printf("create file success\n");
}
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write%d byte to file\n"n_write);
}
char *readBuf;
readBuf = (char *)malloc(sizeof (char)*n_write+1);
lseek (fd,0,SEEK_SET); //参数:(文件描述,偏移值,固定的位置)
//lseek(fd,-11 ,SEEK_CUR); //所在光标位置往前偏移11个
int n_read = read(fd ,readBuf,n_write);
printf("read=%d,context:%s\n",n_write,readBuf);
close(fd);
return 0;
}
1.5 文件操作原理补充
文件描述符:
- 对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX shell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0,1,2这几个数。- 文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。
- 文件描述符的作用域就是当前进程,除了这个进程文件描述符就没有意义了,open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。
Linux系统默认的文件描述符
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(){
int fd;
char readBuf[128];
int n_read = read(0,readBuf,5); // 0 标准输入
int n_write = write(1,readBuf,strlen(readBuf)); // 1 标准输出
printf("\n end!\n");
return 0;
}
运行结果:
文件的操作过程:打开/创建文件-》读取/写入文件-》关闭文件
**文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,**当我们去open打开一个文件时,Linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。read write 都是对动态文件进行操作。
打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和快设备文件中的静态文件就不同步了,当我们close关闭动态文件名,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
之所以这么设计,是因为块(假设有100个字节)设备本身读写非常不灵活,是按块进行读写,最小只读100字节,而内存是按照字节单位进行操作的,比较灵活。
1.6 文件编程练习
自制cp函数:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc,char **argv) //argc为参数个数 不用输入 程序会计算参数输入的数量
{
int fdSrc;
int fdDes;
char *readBuf=NULL;
if(argc!=3) //参数个数<3直接终止程序
{
printf(“pararm 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);
read(fdSrc,readBuf,size); //读取fdSrc中的size个字节到缓冲区 此时光标到了文件末尾
fdDes=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);//打开并清空内容
write(fdDes,readBuf,strlen(readBuf)); //向fdDes中写入数据
close(fdSrc);
close(fdDes);
return 0;
}
配置文件的修改:
文件
例如:
SPEED=5
LENG=100
SCORE=90
LEVEL=95
修改指定内容的思路:
- 找到(要修改的)位置
- (修改的位置)往后移动到(要改的值 )
- 修改要改的值
找寻修改位置时候,用到 strstr() 函数;
功能:用来检索子串在字符串中首次出现的位置
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc , char **argv){
int fdSrc;
char *readBuf=NULL;
if(argc != 2 ){
printf("pararm 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="); //找到(要修改的)位置
//参数1 要找的源文件 2.“要找的字符串”
if(p==NULL){
printf("not found\n");
exit(-1);
}
p=p+strlen("LENG="); //移动字符串个字节
*p='0'; //*p 取内容
lseek (fdSrc,0,SEEK_SET);
int n_write =write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
return 0;
}
1.7 标准C库中的文件操作函数
-
open和fopen的区别
-
来源不同:open是UNIX系统调用函数,返回的是文件描述符,它是文件在文件描述符表里的索引。fopen是ANSIC标准中的c语言库函数,在不同的系统中应该调用不同的内核API,返回值是一个指向文件结构的指针。
-
移植性:fopen是标准C库函数,因此具有良好的移植性;而open函数是由UNIX系统调用,移植性有限。
-
适用范围:open函数返回的是文件描述符,UNIX系统下一切设备都是以文件的形式进行操作;而fopen只能用来操作普通文件。
-
缓冲:
缓冲文件系统:缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读出需要的数据。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器而定。fopen,fclose, fread, fwrite, fgetc, fgets. fputc, fputs, freopen, fseek.ftell, rewind等。
非缓冲文件系统:缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符电、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它.open,close,read, write, getc, getchar, putc, putchar等。
一句话总结一下,就是open无缓冲,fopen有缓冲。前者与read,write等配合使用,后者与freadfwrite等配合使用
使用fopen函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read,write);而使用open函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列的函数快;如果随机访问文件则相反。这样一总结梳理,相信大家对于两个函数及系列函数有了一个更全面清晰的认识,也应该知道在什么场合下使用什么样的函数更合适 效率更高。
-
-
fopen();fwrite();fread();函数说明
FILE fopen (const char *path ,const char *mode);
参数说明:
path :路径
mode :用什么方式打开
返回值:FILE 类型
mode :
mode | 功能说明 |
---|---|
r | 只读方式打开一个文本文件 |
rb | 只读方式打开一个二进制文件 |
w | 只写方式打开一个文本文件 |
wb | 只写方式打开一个二进制文件 |
a | 追加方式打开一个文本文件 |
ab | 追加方式打开一个二进制文件 |
r+ | 可读可写方式打开一个文本文件 |
rb+ | 可读可写方式打开一个二进制文件 |
w+ | 可读可写方式创建一个文本文件 |
wb+ | 可读可写方式生成一个二进制文件 |
a+ | 可读可写追加方式打开一个文本文件 |
ab+ | 可读可写方式追加一个二进制文件 |
size_t fwrite (const void *ptr , size_t size , size_t nmemb , FILE *stream);
参数说明:
- ptr 缓冲区 等同于(buf)
- size 一个字符大小(sizeof char)
- nmemb 个数
- stream (哪个文件) which file
size_t fread (const void *ptr ,size_t size ,size_t nmemb ,FILE *stream);
参数说明:
ptr 缓冲区 等同于(buf)
size 一个字符大小(sizeof char)
nmemb 个数
stream (哪个文件) which file
从一个文件流中读数据,读取count个元素,每个元素size字节.如果调用成功返回count.如果调用成功则实际读取size*count字节
int fseek (FILE *stream , long offset ,int whence);
参数与lseek相同
-
n_fread和n_fwrite的返回值
n_fread 和 n_fwrite 的返回值取决于第三个参数
int n_fwrite = fwrite(str sizeof(char)*strlen(str),1,fp); int n_fread = fread(str sizeof(char)*strlen(str),1,fp); printf("fread=%d,fwrite=%d\n",n_fread,n_fwrite);
运行结果:fread=1,fwrite=1