1、打开文件
(1) int open(const char *pathname, int flags);//文件存在时用这种方式打开
(2) int open(const char * pathname, int flags, mode_t mode);//不存在时用这种方式先创建再打开
2、写入文件
(3)ssize_t write(int fd, const void *buf, size_t count);
(4)close(fd)
3、读取文件
(5)ssize_t read(int fd, void *buf, size_t count);
(6)creat(const char *pathname, int flags);
4、文件内光标移动
off_t lseek(int fd, off_t offset, int whence);
#include
#include
#include
#include
int main()
{
int fd;
fd = open("./file2",O_RDWR);
if(fd<0)
{
printf("open failed\n");
fd = open("./file2",O_RDWR|O_CREAT,0600);
if(fd>0)
{
printf("creat file2\n");
}
}
return 0;
}
打开函数: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选项。来指明新⽂件的访问权限。
O_EXCL:如果同时0_CREAT如果文件存在则创建失败,返回-1。
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中
读取成功返回读到的文件大小
!!!读写文件之前必须先打开文件
五、创建文件
creat(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返回值是从文件头到偏移到当前位置所偏移的值(反应的是文件偏移量)。
int size = lseek(fd,0,SEEK_SET);
巧妙计算文件大小
#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 byte\n",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 success\n");
}
}
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:%s\n",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文件里面内容是:
从这里我们看出,直接对文件写入时,默认是从文件头开始写入数据,会覆盖掉原来的信息。
#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下有三个数作为特定返回值:
0:标准输入
1:标准输出
2:标准错误
#include
#include
#include
int main()
{
char readbuf[20];
read(0,readbuf,10);
write(1,readbuf,strlen(readbuf));//把readbuf里面的值写到标准输出。
return 0;
}
!!!自己实现linux下cp指令:
我们先来了解一下main函数原型:
main 函数的标准原型应该是 int main(int argc, char *argv[]);
argc 是命令行参数的个数。而 argv 是一个指向指针的指针,为什么不是指针数组呢?因为前面讲过,函数原型中的[]表示指针而不表示数组,等价于 char **argv 。这个指针数组分别存放第1–n个命令行参数,参数不限。
#include
int main(int argc,char* argv[3])
{
printf("total params is %d\n",argc);
printf("NO.1 Param is %s\n",argv[0]);
printf("NO.2 Param is %s\n",argv[1]);
printf("NO.3 Param is %s\n",argv[2]);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char* argv[3])
{
char* readbuf = {'\0'};//定义字符串初始化
int fdDes;
int fdSrc;
if(argc != 3)
{
printf("input 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);//文件不存在就创建存在就清空。
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(1,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 error\n");
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 Found\n");
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;
}
文件缓冲区不止会写字符,也会写数字,只会影响人类的判断,不会影响机器的判断。
但是文件内部却是这样的,因为我们查看文件时,看到的都是字符,这里我们写入的是真实的数字,读取时也是%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 %d\n",n_write);
printf("fread is return %d\n",n_read);
printf("test1.a = %d test1.b = %c\n",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<len;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;
}