文件编程内容比较多,如文件系统原理及访问机制文件在内核中的管理机制,什么是文件信息节点iNode、文件共享、文件权限、各种用户对其权限等等。以下主要记录如何用代码操作文件,实现文件的创建、打开、编辑等自动化执行。
在介绍相关的操作之前,我们要记住几个概念,这样方便我们后续的理解
linux一切皆文件:硬件设备、管道、数据库、socket等
文件描述符:在linux下一切皆文件,文件描述符是内核为了高效的管理已经被打开的文件所创建的索引,它是一个非负整数,用于指代被打开的文件,所有执行I/O操作的系统调用都是通过文件描述符完成的。 在linux中,进程是通过文件描述符(file descriptors 简称fd)来访问文件的,文件描述符实际上是一个整数。在程序刚启动的时候,默认有三个文件描述符,分别是:0(代表标准输入),1(代表标准输出),2(代表标准错误)。再打开一个新的文件的话,它的文件描述符就是3。 POSIX标准规定,每次打开的文件时(含socket)必须使用当前进程中最小可用的文件描述符号码。
文件描述符的创建: 进程获取文件描述符最常见的方法就是通过系统函数open或create获取,或者是从父进程继承。进程相关知识这里就不展开叙述,前两者往下看。
在windows下,我们一般编写一个文档都是,创建word文档——>打开文档——>编辑文档——>保存文档——>关闭文档。
同理,linux下任何操作都离不开这几个步骤
操作系统提供了相关上述步骤的API,如下:
1、打开/创建文件
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include<fcntl.h>
函数:
打开:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t made);
第一个参数是要打开的文件名(含路径,缺省为当前路径)
第二个参数Flags是对文件操作的权限
1、O_RDONLY:只读打开
2、O_WRONLY:只写打开
3、O_RDWR:可读可写打开
当我们附带了权限后,打开的文件只能按照这种权限来操作
以上三种常数中应当只指定一个,下列常数可选择:
O_CREAT:若文件不存在则创建它。使用此选项时,需要同时说明第三个参数mode(也就是第二个函数的写法),用其说明该新文件的存取许可权限。
O_EXCL:如果同时指定了O_CREAT,而文件已经存在则出错返回值是-1。 O_APPEND:每次写时都加到文件的尾端。如果不加这个常量每次写的时候就会将源文件内容覆盖掉一定的字节数(写入多少字节数就在源文件覆盖多少字节)如果用或操作加上这个常量,会在原文件另起一行,将东西写入。
O_TRUNC:属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0。 就是将源文件中的所有内容都干掉,就没有内容了。
创建:
int create(const char *filename, mode_t mode)
第一个参数:要创建的文件名
第二个参数:创建模式(可读可写可执行)如下:
S_IRUSR 4 可读
S_IWUSR 2 可写
S_IXUSR 1 可执行
S_IRWXV 7 可读可写可执行
举个例子:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd1;
int fd2;
fd1=open("./file1",O_RDWR);//"./file1"是字符串,而字符串本身就是指针,符合open函数对参数的要求
printf("fd1=%d\n",fd1);//文件存在返回描述符,不存在文件返回-1
if(fd1==-1){
printf("打开失败\n");
//以下方式可以在文件不存在的时候创建文件
fd2=open("./file1",O_CREAT|O_RDWR,0600);//在当前路径创建文件
printf("fd2=%d\n",fd2);
if(fd2>0){
printf("文件创建成功\n");
}
}
return 0;
}
从上面我们会发下,open函数如果调用,打开一个存在的文件,则会返回一个fd(file description),这就是前面介绍文件描述符及open函数创建文件描述符的方式,create函数亦是同理
在终端执行ls -l命令可查看文件的所有者和文件类型、文件最后修改时间等等
我们随意拿一条来说一下ls -l执行后的东西每样所表示的意思
eg:-rwxr-xr-x 1 clc book 8427 Fab 9 22:59 a.out
第一个-rwxr-xr-x可以分成 - rwx r-x r-x来看
-:代表该文件为普通文件(如果是d,表示目录;l表示符号链接)
rwx:这里其实可以和后面的一起分为三类,给予不同类相关的权限,这类代表文件所有者的权限(r:可读;w:可写;x:可执行)
r-x(第一个):这类代表组用户中同组权限
r-x(第二个):这类代表组用户中其他组的权限
回到-rwxr-xr-x 1 clc book 8427 Fab 9 22:59 a.out
1:代表连接的文件数
clc:代表用户
book:代表用户所在的组
8427:代表文件大小
Fab 9 22.59:文件修改的最后日期
a.out:文件名
fd2=open("./locallinuxfile1",O_CREAT|O_RDWR,0600);
这行代码中的0600是什么意思?
一般地文件操作的权限就试一下几种:
1、可读 r 4
2、可写 w 2
3、可执行 x 1
0600其中的6表示4+2就是可读可写的权限
2、写入文件函数
头文件:
#include <unistd.h>
函数:
ssize_t write(int fd, const void *buf, size_t count);
第一个参数:文件描述符
第二个参数:缓冲区(无指针),
第三个参数:写入文件的字节数。
这个函数可以简单理解为:将buf指向的缓冲区中的count个字节写入到文件中。
如果写入成功返回整形数(写入文件的字节数),写入失败则返回-1。
示例:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
int rebuf;
char* buffer=NULL;
buffer=(char*)malloc(128);
printf("请输入要写入文件的内容:\n");
scanf("%s",buffer);
fd=open("./file1",O_RDWR|O_CREAT,0600);
if(fd==-1){
printf("文件打开失败\n");
perror("open");
}
rebuf=write(fd,buffer,strlen(buffer));//将内容写入文件
close(fd);//关闭文件
printf("写入文件的字节数是:%d\n",rebuf);
return 0;
}
3、读取文件函数
头文件:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
第一个参数:文件描述符
第二个参数:缓冲区
第三个参数:从文件里面读多少字节到缓冲区中
read函数读的时候是从光标向后读指定的字节数。
示例:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
int readbuf;
char* buffer;
buffer=(char*)malloc(128);
fd=open("./file1",O_RDWR|O_CREAT);
if(fd==-1){
printf("文件打开失败\n");
perror("open");
}
readbuf=read(fd,buffer,4);
printf("从文件中读取的字节是:%s,字节数是:%d\n",buffer,readbuf);
return 0;
}
4、文件光标的移动操作
如果我们在同一个代码中先后执行write函数和read函数,read输出出来是没有东西的,这是因为这个时候文件光标在文件末尾,后续是没有任何输出的东西,这个时候需要将光标移动到前面,有两种方法操作
第一种:
在文件write之后,先close();再open();之后再调用read()函数
第二种:
调用lseek函数
5、lseek函数
头文件:
#include <sys/typs.h>
#include<unistd.h>
函数:
off_t lseek(int fd, off_t offset, int whence);
第一个参数:文件描述符
第二个参数:偏移值(“-”+ 偏移值,如果是0就不需要加前缀,负值表示往前偏移,正值表示往后偏移)
第三个参数:表示偏移量相对于哪个位置进行偏移
返回值是相对文件头的偏移量,可以间接的用lseek计算文件的大小,只需将光标指向文件尾部。 通常第三个参数有以下几个宏:
SEEK_SET:指向文件的头
SEEK_CUR:指向文件的当前位置
SEEK_END:指向文件的尾
注:因为lseek()返回的是文件从头到光标所在地便宜的offset,所以可以利用这一点算文件大小
lseek(fd, 0, SEEK_END);
综合示例:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
int n_write;
int n_read;
char* writebuffer;
char* readbuf;
writebuf=(char*)malloc(128);
readbuf=(char*)malloc(strlen(writebuffer)+1);
printf("请输入要写入的内容:\n");
scanf("%s",writebuffer);
fd=open("./file1",O_CREAT|O_RDWR,0600);
if(fd==-1){
printf("文件打开失败\n");
perror("open");
}
n_write=write(fd,writebuffer,strlen(writebuffer));
if(n_write==-1){
printf("文件写入失败\n");
perror("write");
}
lseek(fd,0,SEEK_SET);
n_read=read(fd,readbuf,n_write);
if(n_read==-1){
printf("文件读取失败\n");
perror("read");
}
close(fd);
printf("文件中写入了:%s,字节数是:%d\n",writebuffer,n_write);
printf("从文件中读取了:%s,字节数是:%d\n",readbuf,n_read);
return 0;
}