linux系统编程之文件编程

一、open函数创建及打开文件

        用一个函数之前我们必须了解其原型,在linux环境下在我们用CTRL+ALT+T调出终端,在终端界面输入man 2 open就可以看到对open函数的说明:

1.open函数原型:int open(const char *pathname, int flags);

                             int open(const char *pathname, int flags, mode_t mode);

2.参数1:const char *pathname,该文件路径;

   参数2:int flags, 打开文件的三种模式O_RDONLY(可读),  O_WRONLY(可写),  or                 O_RDWR(可读可写),三者选其一;

                另外,选择以上其中一个参数后还可以选以下参数:

                O_CREAT 若文件不存在则创建它。使用此选项时,需同时说明参数3mode,其

                说明该新文件的存取许可权限;             
                O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则返回-1;
                O_APPEND 每次写时都加到文件尾端;
                O_TRUNC 属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或者

                只写成功打开,则将其长度截短为0。

3.返回值:如果文件成功创建或打开则返回文件对应的文件描述符(大于0的整数),否则返

                 回-1。(其中linux系统中默认0-标准输入,1-标准输出,2-标准错误)

4.包含特定头文件:#include
                                #include
                                #include

        我们在代码上体现一下:

linux系统编程之文件编程_第1张图片

打印出来的文件描述符为3,说明成功打开了file1,当然这里是因为我提前用touch file1创建了file1, 那如果文件不存在呢?

linux系统编程之文件编程_第2张图片

在原本代码上添加了O_CREAT,并且说明0600权限,其作用就是没有该文件就创建该文件,我先通过rm file1把之前创建的file1给删掉了,在执行修改后的代码,从输出结果可以看到打开不存在的文件会返回-1,再通过O_CREAT创建了该文件。

二、write函数进行文件写入操作 

        同样的,我们先对write函数原型进行了解:

1.write函数原型:ssize_t write(int fd, const void *buf, size_t count);

2.参数1:int fd,文件描述符即open函数的返回值;

   参数2:const void *buf,写入到fd文件内容的写入缓冲区;

   参数3:size_t count,要写入内容的字节数;

3.返回值:正确写入则返回写入字节大小,没有内容写入则返回0,错误写入则返回-1;

4.包含特定头文件:#include

        我们来看看怎么使用write函数:

linux系统编程之文件编程_第3张图片

从输出结果可以看到我们已经正确写入了buf写入缓冲区里面的内容到file1里面了,再到终端界面通过vi file1来打开文件:

在file1里面确实有我们刚刚写入的字节。 

        补充一下:每次打开了文件之后要调用close函数来关闭文件,否则会导致文件的破坏内容的丢失,close函数很简单,参数就是要关闭的文件的文件描述符,返回0则成功关闭,-1则未成功关闭。这里就不展开讲了。

三、read函数进行文件读操作

        有写操作当然少不了读操作,我们也来看看其说明:

1.函数原型:ssize_t read(int fd, void *buf, size_t count);

2.参数1:int fd,要读文件的文件描述符;

   参数2:void *buf,存放读出来内容的缓冲区;

   参数3:size_t count,从fd文件中读内容的字节大小;

3.返回值:正确读取则返回读取到内容的字节大小,没有内容则返回0,错误读取则返回-1;

4.包含特定头文件:#include

我们同样来看看代码:

linux系统编程之文件编程_第4张图片

前面写操作相关代码不变,在写操作后面加入上面这些读操作相关的代码,要注意的是malloc的时候sizeof那里最好加个1,用来存放\0,因为strlen不会将\0计算进去。按道理应该可以输出我们想要的结构,我们来看一看:

我们发现结果是一个空的,读取的字节也不是24,这是为什么呢?因为我们在进行写操作之后并没有关闭该文件,光标还是停留在文件中内容的最后面,所有读文件中的内容时啥也读不到,我们对代码进行改进:

linux系统编程之文件编程_第5张图片

我们在进行读操作之前关闭该文件再重新打开,光标就回到内容的开头了,也就可以正确读到内容:

四、lseek函数进行光标的移动     

        前面我们进行读操作时就因为光标问题导致读取不成功,其实我们可以通过lseek函数来移动光标,我们来看看其说明:

1.函数原型:off_t lseek(int fd, off_t offset, int whence);

2.参数1:int fd,要操作文件的文件描述符;

   参数2:off_t offset,光标的偏移量;

   参数3:int whence,光标的起始位置;

3.返回值:返回针对文件开头开始计算的偏移量,出错返回-1;

4.包含特定头文件:#include
                                #include

刚刚关闭文件再打开文件的操作可以有以下代码代替:

linux系统编程之文件编程_第6张图片

光标的偏移量(off_t offset)为正数时表示往前偏移,为负数时表示往后偏移,经过修改后也可以正确得到我们想要的结构。

        运用lseek函数我们还能巧妙的计算文件内的字节大小:

这里文件的字节(filesize)为25是因为字符串中的\0也占一个字节。

五、open函数的补充

        (open 打开的是静态文件,会生成一个文件结构体进而静态文件编程动态文件;write,read操作的是动态文件,改变的是结构体中的缓冲区;close文件后,该文件又变成静态文件,存放到磁盘中)

        在有了读写操作之后open函数的很多功能才得以实现:

1.O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则返回-1;

我们在已存在file1的基础上运行一下代码:

linux系统编程之文件编程_第7张图片

可以看到创建文件失败。

 2. O_APPEND 每次写时都加到文件尾端;

我们先在file1文件内写入其他内容,在运行一下代码:

第一个字符串使我们提前写入的,第二个字符串是我们通过O_APPEND方式打开文件的,所有没有覆盖掉之前的内容。大家可能对前面编译后运行的结果有疑问,为什么n_read和filesize相差这么大,因为我们读的字节数就是我们写入的字节数,我们只写入了一次。

3.O_TRUNC 属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或者只写成功打开,则将其长度截短为0

我们现在file1文件中是有内容的(上面代码所产生),我们运行一下代码看看:

linux系统编程之文件编程_第8张图片

 可以看到现在写入的内容覆盖了原本的内容。

 源代码入下:

#include 
#include 
#include 
#include 
#include 
#include 
 #include 

int main()
{
	int fd;
	//char *writebuf = {"Champion of the Warriors"};
	char *writebuf = {"huuggulm"};
//4	fd = open("./file1",O_RDWR|O_TRUNC);
//3	fd = open("./file1",O_RDWR|O_APPEND);
//2	fd = open("./file1",O_RDWR|O_CREAT|O_EXCL,0600);
/*1*/	fd = open("./file1",O_RDWR);
/*2	if (fd == -1)	//O_EXCL
	{
		printf("fd = %d,file alread exists\n",fd);
		return 0;
	}
2*/
/*1*/	if (fd == -1)	//O_RDWR	
	{
		printf("fd = %d,open file1 failed\n",fd);
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if (fd > 0)
		{
			printf("fd = %d,create and open file1 success\n",fd);
		}

	}	
/*1*/
	printf("begin write operation\n");
	//ssize_t write(int fd, const void *buf, size_t count);	
	int n_write = write(fd,writebuf,strlen(writebuf));
	if (n_write > -1)
	{
		printf("n_write = %d,write success\n",n_write);
	}

	//close(fd);
	//fd = open("./file1",O_RDWR);

	lseek(fd,0,SEEK_SET);
	//lseek(fd,-n_write,SEEK_END);
	//lseek(fd,0,SEEK_END);

	printf("begin read operation\n");
	char *readbuf = NULL;
	readbuf = (char*)malloc(sizeof(char)*n_write+1);
	//ssize_t read(int fd, void *buf, size_t count);
	int n_read = read(fd,readbuf,n_write);
	printf("n_read = %d,content is %s\n",n_read,readbuf);

	//off_t lseek(int fd, off_t offset, int whence);
	int filesize = lseek(fd,0,SEEK_END);
	printf("filesize is %d\n",filesize);

	close(fd);

	return 0;
}

六、自己编写CP指令代码

        在命令终端我们可以通过cp xxx.c vvv.c实现源文件的拷贝,在有了读写操作的基础上我们可以自己实现cp指令,当然在此之前我们先简单了解一下main函数中的参数问题:

linux系统编程之文件编程_第9张图片

linux系统编程之文件编程_第10张图片

可以看到./a.out就是第一个参数,后面便是第二和第三个参数,这里**argv是字符指针的数组,即字符数组中存放字符数组,为了方便理解,我在下面举个例子:

linux系统编程之文件编程_第11张图片

看到这大家应该就比较容易理解了。

        在以上基础上,我们进行cp指令代码的编写:

linux系统编程之文件编程_第12张图片

代码中argv[1]就是demo7CP.c,argv[2]就是new.c,代码执行后,用ls指令可以看到当前文件夹下的文件,我们可以发现多了new.c,并且其内容的demo7CP.c一样。

七、修改配置文件

        修改配置文件顾名思义就是在已有的文件里修改某一参数,代码如下:

linux系统编程之文件编程_第13张图片

 linux系统编程之文件编程_第14张图片

这里要注意几个地方:1../a.out后面记得加文件名,因为我们在open函数里没写文件名,写得时argv[1],所以我们得在编译时写上;2.灵活使用lseek函数获得文件的总字节大小,以便选择合适的readbuf大小;3.使用strstr函数,找到对应要修改配置信息的位置,并且指针开始指向被查找的字符串的开头。

八、写整型数或结构体到文件

        前面我们读写操作都是针对字符类型的,如果写其他类型的到文件呢?

linux系统编程之文件编程_第15张图片

打印出来的结果是我们想要的,但是要是打开该写入的文件就会发现里面的内容是看不懂的,但是无伤大雅,机器是看的懂的。 

九、补充

        这里补充一点内容,就是fopen、fread等一些ANSIC标准C的的API和open等UNIX系统的API的区别。

1.层次不同
open是系统调用,返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,
fopen是ANSIC标准中的C语言库函数,返回的是一个指向文件结构的指针)。在不同的系统中应该调用不同的内核api。linux中的系统函数是open,fopen是其封装函数,fopen的实现要调用open。标准C库.
2.fopen和open最主要的区别是是否有缓存
fopen用户态下就有了缓存,它使用了FILE这个结构保存缓冲数据。在进行read和write的时候减少了用户态和内核态的切换。
open没有缓存,每次读操作都直接从文件系统中获取数据。在进行read和write的时候每次都需要进行内核态和用户态的切换。
表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列快;如果随机访问文件open要比fopen快。
3.一般用fopen打开普通文件,用open打开设备文件。

我们前面所用的open等函数都可以用fopen等函数来替换:

linux系统编程之文件编程_第16张图片

linux系统编程之文件编程_第17张图片

具体函数就不展开讲了,大家稍作了解即可。feof这个函数就是判断文件是否到达最后,到达最后返回非0,否则返回0。

        以上就是linux系统编程中文件编程的大致内容,自己也是刚学,有不对的地方欢迎大佬批评指正,希望这篇文章对大家有所帮助。

 

你可能感兴趣的:(开发语言,linux,c语言)