linux系统编程--文件IO

文件操作

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

read();

函数原型:ssize_t read(int fd, void *buf, size_t count);
函数功能:从fd指向的文件中读取最多count个字节的数据,存放在buf指向的缓存区
  参数分别是:从哪读,读到哪 ,读多少,
形参列表:
  fd:文件描述符 --> 指向要操作的文件
  buf:缓存区首地址 --> 用来存放从文件中读取出来的数据
  count:从文件中读取数据的上限
返回值:
  成功:实际读取的字节数
  失败:-1

实现cat命令

#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

write();

函数原型: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;
}

Close();

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:对文件进行读写操作时,文件偏移指针是否是同一个?
使用同一个文件描述符对文件进行读写操作时,文件偏移指针共用的!

  1. 文件偏移指针定位
    文件偏移指针指定下次文件读写操作的起始位置!
    有时候,需要自己定位文件偏移指针的位置,选择下一次操作的文件内容。
    文件偏移指针定位函数: lseek();

lseek();

函数原型: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;
}

通过调用read函数,实现cat命令的功能。(能够读取任意大小的文件)

#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;
}

通过调用read/write函数,实现cp命令的功能
#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;
}

你可能感兴趣的:(linxu系统编程)