文件的写入(write)
昨天已经学习了文件的打开及创建,及使用open函数,传递不同的参数可以实现文件的打开及创建,并赋予文件的权限,今天延续昨天的学习内容。
文件的写入及读取都需要添加一个头文件:
#include <unistd.h>
先说说文件的写入函数:write。
write() 文件写入函数
函数原型: ssize_t write(int fd, const void *buf, size_t count);
返回值: 返回实际写入数据的大小,如果count 的值远远大于buf的大小,返回的只有buf的 大小值。
说明: 写入 count个字节的 buf给 fd描述符指向的文件。
用法: 把open打开或创建的文件的描述符传给 write的 fd,并且定义一个指针传给 buf,count为buf的大小,可以用strlen()函数计算buf的大小。例如:
#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="很帅!";
fd=open("./file",O_RDWR);
// 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);
}
}
运行结果:
打开了file之后:
再说说read函数
read() 文件读取函数
函数原型:ssize_t read(int fd, void *buf, size_t count);
返回值: 返回读取fd指向文件的 count个字节
说明: 读取fd指向的文件的count个字节的内容,存入buf
用法: 把open的返回值(即fd文件描述符)传递给read的fd,定义一个指针来存放buf的内容,count赋值大小。
#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="很帅!";
fd=open("./file",O_RDWR);
int n_write=write(fd,buf,strlen(buf));//先写入内容
// ssize_t read(int fd, void *buf, size_t count);
close(fd); //先关闭文件
fd=open("./file",O_RDWR);//再打开文件
char *Read_Buf;
Read_Buf=(char *)malloc(sizeof(char)* n_write+1);//分配空间给指针
int n_Read=read(fd,Read_Buf,n_write);//读取内容
printf("Read %d byte to file,%s\n",n_Read,Read_Buf);
close(fd); //关闭文件
return 0;
}
在程序中可以看到,我们在write之后并没有直接read,那是因为read需要文件内的 光标在我们所要读取的内容之前(这里为文件的头部),而write后的光标总是在文件的尾部,所以直接在write之后读取的话,不会读取到内容,返回值为0。但是先关闭文件(close)再打开文件(open)后,光标就在文件的头部,这样才能正确的读取。但是这个操作光标的方法不仅土得掉渣,还不灵活,这就需要光标操作函数 lseek()
lseek() 光标操作函数
函数原型:off_t lseek(int fd, off_t offset, int whence);
返回值: 返回一个相对于文件头的偏移值;
参数说明:
fd : 需要操作光标的文件的描述符(非负整数);
offset :相对于whence的偏移值:负数——往前偏移、正数——往后偏移;
whence :光标的位置设定:SEEK_SET 文件头部、SEEK_END文件尾部、SEEK_COUNT当前位置。
用法: 在fd指向的文件中,光标从whence的位置,往前或者往后偏移offset个字节。
#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="很帅!";
fd=open("./file",O_RDWR);
// 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);
}
// off_t lseek(int fd, off_t offset, int whence);
lseek(fd,-6,SEEK_END);//从尾部向前偏移6个字节
// ssize_t read(int fd, void *buf, size_t count);
char *Read_Buf;
Read_Buf=(char *)malloc(sizeof(char)* n_write+1);
int n_Read=read(fd,Read_Buf,n_write);
printf("Read %d byte to file,%s\n",n_Read,Read_Buf);
close(fd);
return 0;
}
也可以利用lseek函数来计算文件的大小,这么用:
int n_offset=0;
n_offset=lseek(fd,0,SEEK_END);//
因为lseek的返回值是相对于文件头的偏移值,所以lseek从头部到尾部的偏移值就算出来了。
close() 关闭文件函数
函数原型: int close(int fd);
说明: 关闭fd指向的文件;
当我们从open到write/read,后一定要用close来关闭文件,这样才完成操作文件的基本流程,也防止程序出错及文件丢失。至于为什么,这涉及到文件操作的原理。
文件的操作原理
文件描述符
1. 文件描述符是一个非负整数,当打开一个现存的文件或创建一个新文件时,内核会向进程返回一个文件描述符,当读写一个文件时,用open()或者creat返回的文件描述符标识该文件,作为参数传递给write和read。
2. 在UNIX Shell中,文件描述符有3个已经定义的宏,它们分别替代了0、1、2:
STDIN_FILENO: 替代了 0 :为标准输入文件,传递给read的时候,可以获取键盘的输入,作用相当于scanf;
STDOUT_FILENO: 替代了 1:为标准输出文件,传递给write 可以打印输出,相当于printf;
STDRR_FILENE: 替代了 2 :为标准错误文件,以后可以用来作错误提示。
这就解释了为什么我们在创建文件的时候,文件描述符总是先从3开始。
3. 文件描述符的作用域是当前的进程,出了这个进程后,这个文件描述符就没有意义了,也就是说这个文件描述符不起作用了。
静态文件及动态文件
静态文件
静态文件存于磁盘当中,比如我们电脑桌面的文件夹,就属于静态文件。
动态文件
当我们open一个静态文件之后,Linux内核会产生一个结构体来描述这个文件,结构体当中可能文件名,文件大小、文件内容等元素;而其中的文件内容就是我们在使用 write 或者read所操作的文件,就是动态文件。它在close(关闭文件 )之前会存放在内存当中,直到执行close之后,才会存放在磁盘当中,变成静态文件。这就是为什么我们在write/read之后要close的原因。