inux中一切皆文件!
只要掌握了文件操作,即可对linux下所有资源进行操作。
文件操作就是对文件进行 读 写 操作。
对文件进行操作时,文件名是不能明确指定一个文件空间的,需要根据文件名首先找到对应inode号。
一个inode号对应着一个固定的文件空间,对文件操作时,使用inode号进行操作,才能保证操作无误!
对一个文件基本的操作,只有:读、写
读:把文件内容读取出来,查看文件内容
写:编辑文件,修改文件内容。
在对文件进行读写操作之前,可以使用文件偏移指针指定要操作文件的哪一块空间。
总结:对文件的操作方式有:读、写、偏移!!!
当想要对一个文件进行操作的时候,首先指定要操作文件的文件名。因为文件名不能准确的表示一块文件空间,所以在指定好要操作的文件名之后,通过调用open()函数,打开文件。该操作可以根据提供的文件名得到对应的inode号,然后对inode号进行封装,得到操作该文件的文件结构体,文件结构体在应用程序中表现为文件描述符。
接下来,利用open()之后得到的文件描述符,系统就可以找到要操作的文件空间,然后通过调用文件操作偏移指针定位函数lseek(),指定要操作文件中的哪一块空间,指定好之后,就可以对文件进行读写操作了,读写操作调用read()/write()函数。当对文件操作完成之后,关闭文件即可。
对文件操作中,所有的信息全部都存放在文件结构体中,文件描述符就是指向文件结构体的指针。
1)文件描述符是操作一个文件的节点。
2)应用程序中每打开一个文件,就会得到文件的文件结构体,结构体指针存放在应用程序中自动定义的文件描述符数组中,按照数组的下标从小到大依次存放。
3)文件描述符本质上是一个数组的下标。
4)文件描述符的范围是:0 ~ 1023
5)当应用程序运行起来,文件描述符数组空间分配,应用程序结束,文件描述符空间释放。
6)文件描述符中存放有对文件的操作方式。
7)文件描述符数组中前面三个元素在应用程序运行时立即自动占用。
0:标准输入 键盘 1:标准输出 屏幕 2:标准异常 屏幕
8)进程打开的第2+x个文件,文件描述符不是ionde号
对一个文件进行操作,第一步必须先打开文件,得到文件对应的文件描述符。
打开文件:open();
函数原型:int open(const char *pathname, int flags, mode_t mode);
函数功能:根据提供的带路径的文件名(pathname),得到对应的文件描述符,通过返回值返回。
形参列表:
pathname:带路径的文件 --> 指定要操作的文件
flags:对当前文件的操作方式
三个务必选一个!!!
O_RDONLY :
只读
O_WRONLY :
只写
O_RDWR :
读写
可选!
O_CREAT :创建文件(如果文件不存在,则创建。如果文件存在,则跳过创建,直接打开)
O_EXCL :检测文件是否存在(和O_CREAT结合使用,如果文件不存在,则创建,如果文件存在,出错)
O_TRUNC :清空写
O_APPEND :追加写
O_WRONLY :只写
O_TRUNC :清空写
O_APPEND :追加写
O_RDWR :读写
O_TRUNC :清空写 trunc
O_APPEND :追加写
O_CREAT creat
代码写的权限经过掩码操作后的才是最后的权限。
umask 默认为0002(八进制) 000 000 010 (二进制)
0777 & ~(umask)
111 111 111
111 111 101
mode:文件权限 (文件对不同用户的权限)
0764
当创建新文件时,该参数必须。
当打开已存在的文件时,该参数不需要。
返回值:
成功:指定的文件对应的文件描述符 – fd
失败:-1
注释:文件权限和权限掩码进行运算,得到最终的文件权限
权限掩码:umask
思考01.文件如果不存在,是否创建
创建: O_CREAT
是否需要检测?
思考02.如果文件存在,我要对文件进行什么操作?
只读:O_RDONLY
只写:O_WDONLY
读写:O_RDWD
思考03.如果是写操作,怎么写
覆盖:
追加:O_APPEND
小练习:
调用open函数,编程实现touch命令的功能。
分析touch
touch新建一个文件
例如: touch abc.c
argc=2
argv:”touch”,abc.c
当打开一个文件,得到文件描述符之后,就可以对文件进行读写操作了。
#include
#include
#include
#include
#include
int main (int argc,char *argv[])
{
if (argc !=2)
{
printf ("请输入想要创建的文件名\n");
}
int fd=open(argv[1],O_RDONLY|O_CREAT,0666);
close(fd);
return 0;
}
测试:
[root@local 01_file_io]# ls
my_touch.c
[root@local 01_file_io]# gcc my_touch.c -o my_touch.exe
[root@local 01_file_io]# ls
my_touch.c my_touch.exe
[root@local 01_file_io]# ./my_touch.exe
请输入想要创建的文件名
[root@local 01_file_io]# ./my_touch.exe test.txt
[root@local 01_file_io]# ls
my_touch.c my_touch.exe test.txt
函数原型:ssize_t read(int fd, void *buf, size_t count);
函数功能:从fd指向的文件中读取最多count个字节的数据,存放在buf指向的缓存区
参数分别是:从哪读,读到哪 ,读多少,
形参列表:
fd:文件描述符 --> 指向要操作的文件
buf:缓存区首地址 --> 用来存放从文件中读取出来的数据
count:从文件中读取数据的上限
返回值:
成功:实际读取的字节数
失败:-1
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = 0;
fd = open(argv[1], O_RDONLY);
if(fd < 0)
{
printf("open %s failed!\n", argv[1]);
return -1;
}
int ret = 0;
char buf[32] = {0};
ret = read(fd, buf, sizeof(buf));
printf("ret: %d\n", ret);
printf("buf: %s\n", buf);
return 0;
}
执行结果:
my_cat.c my_cat.exe text.txt
[root@local read]# ./my_cat.exe text.txt
ret: 4
buf: 123
函数原型:ssize_t write(int fd, const void *buf, size_t count);
函数功能:从buf指向的缓存区中读取数据,向fd指向的文件中写。最大写入count个字节的数据。
形参列表:
fd:文件描述符 --> 指向要操作的文件
buf:缓存区首地址 --> 指向存放要写入文件的数据的空间
count:最大写入文件的字节数 (实际写入的字节数)
写到哪个文件(文件描述符)
写什么内容(buf)
写多少个(sizeof)
返回值:
成功:实际写入文件的数据字节数
失败:-1
注释:写的方式有三种 --> 覆盖写、清空写、追加写
注意:
1)count传参的值就是实际写入文件的字节个数,如果可见字符不足,用0补充!
2)打开文件时,有写(只写/读写)操作方式,写的方式就是覆盖写。
3)打开文件时,有O_TRUNC操作方式,写的方式就是清空写。
4)打开文件时,有O_APPEND操作方式,写的方式就是追加写。
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = 0;
fd = open(argv[1], O_WRONLY | O_CREAT | O_APPEND, 0666);
char buf[32] = "how are you 你是谁";
write(fd, buf, strlen(buf));
return 0;
}
int close(int fd);
成功返回0;失败返回-1;
文件操作:
文件描述符!
open(); read(); write(); lseek(); close
文件描述符:
1)文件描述符是操作一个文件的句柄!
2)文件描述符是应用程序中自动创建的文件描述符数组的数组下标
3)文件描述符的0/1/2已经被占用。
4)在应用程序中打开一个文件,得到的文件描述符是从3开始,从小到大连续分配的整型数据。
5)应用程序运行,文件描述符数组分配,应用程序结束,文件描述符数组释放。
6)文件描述符中存放有文件的操作方式。
7)在执行读写操作时,文件偏移指针会自动往后偏移。
8)文件描述符中存放着文件操作的偏移指针。
9)在一个应用程序打开同一个文件多次,可以得到多个相互独立的文件描述符。
每一个文件描述符中都有自己的操作方式及文件偏移指针。
10)文件描述符的取值范围为:0 ~ 1023。
11)文件描述符是一种个数有限的,可重复利用的资源!
12)每次打开文件都会得到最小的、空闲的文件描述符。
三种写操作,写的特性在open一个文件的时候生效!
以追加写方式进行操作, 写操作时从文件末尾开始写,如果执行读操作,则从文件开头开始读!
文件偏移指针的特性:
1)在读写操作时,文件偏移指针会自动偏移
2)文件偏移指针指定文件操作的位置
3)读写操作共用文件描述符中的同一个文件偏移指针
4)同一个文件可以打开多次,得到不同的文件描述符,每一个文件描述符中都有一个独立的偏移指针。
问题1:文件偏移指针存放在哪?
每一个文件描述符都有一个独立的文件偏移指针! (一个文件打开两次,得到两个文件描述符,分别对文件操作)
问题2:对文件进行读写操作时,文件偏移指针是否是同一个?
使用同一个文件描述符对文件进行读写操作时,文件偏移指针共用的!
函数原型:off_t lseek(int fd, off_t offset, int whence);
函数功能:对文件偏移指针进行重新定位
形参列表:
fd:文件描述符 – 要操作的文件
offset:相对于基准点的偏移距离 (负数:向前偏移 正数:向后偏移)
whence:文件偏移指针定位的基准点
SEEK_SET :文件开头
SEEK_CUR:文件偏移指针当前位置
SEEK_END:文件结尾
返回值:
成功:文件偏移指针当前位置到文件开头的距离
失败:-1
注释:可以把文件偏移指针定位到文件结尾,这时lseek函数的返回值就是文件大小!
使用read/write函数,实现文件的加密/解密。
#include
#include
#include
#include
#include
void jm(char* p, int count);
void dejm(char* p, int count);
int main(int argc, char* argv[])
{
int fd = 0;
int fp = 0;
fd = open(argv[1], O_RDONLY);
fp = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);
if((fd < 0) || (fp < 0))
{
printf("open failed!\n");
return -1;
}
int ret = 0;
char buf[32] = {0};
do
{
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
if(strcmp(argv[3], "-j") == 0)
{
printf("---加密---\n");
jm(buf, ret);
}
else if(strcmp(argv[3], "-d") == 0)
{
printf("---解密---\n");
dejm(buf, ret);
}
else
{
printf("---复制---\n");
}
write(fp, buf, ret);
}
while(ret > 0);
return 0;
}
void jm(char* p, int count)
{
int i = 0;
for(i = 0; i < count; i++)
{
p[i] = p[i] + i*(i+2); //加密算法
}
}
void dejm(char* p, int count)
{
int i = 0;
for(i = 0; i < count; i++)
{
p[i] = p[i] - i*(i+2);
}
}
翻倍
1)灵活使用lseek函数,实现文件的内容翻倍。
只能用一个文件描述符,只能用一个文件。
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = 0;
fd = open(argv[1], O_RDWR);
if(fd < 0)
{
printf("open failed!\n");
return -1;
}// 1. 得到文件大小
int length = 0;
length = lseek(fd, 0, SEEK_END);//文件总长度
lseek(fd, 0, SEEK_SET);
int roff = 0;
int ret = 0;
char buf[32] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
lseek(fd, roff, SEEK_SET);
ret = read(fd, buf, sizeof(buf));//每次读32个字节
roff += ret;
if(roff >= length)
{
ret = roff - length; //超出
ret = sizeof(buf) - ret;
lseek(fd, 0, SEEK_END);
write(fd, buf, ret);
break;
}
lseek(fd, 0, SEEK_END);
write(fd, buf, ret);
}
return 0;
}
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = 0;
fd = open(argv[1], O_RDONLY);//argv[0]为函数本身
if(fd < 0)
{
printf("open %s failed!\n", argv[1]);
return -1;
}
int ret = 0;
char buf[32] = {0};
do
{
memset(buf, 0, sizeof(buf)); //重复读,函数指针会自动偏移
ret = read(fd, buf, sizeof(buf)-1);
printf("%s", buf);
}while(ret > 0);//不够严谨
//while(1)
//{
// ret =read(fd,buf,128);
//if(ret<128)//读不够128个字节了,结束
// {
// printf(“%s,buf”);
// break;
// }
// printf("%s", buf);
//}
return 0;
}
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = 0;
int fp = 0;
fd = open(argv[1], O_RDONLY);
fp = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);//需要创建的时候就要写权限
if((fd < 0) || (fp < 0))
{
printf("open failed!\n");
return -1;
}
int ret = 0;
char buf[32] = {0};
do
{
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
write(fp, buf, ret);
}while(ret > 0);
return 0;
}