IO学习三(fseek、ftell、rewind、fflush、 getline、临时文件)

我们现在要做这样一件事:
fp=fopen();
打开一个文件
fputc(fp) 10;
每次写进一个字符,写10次,就往文件中写了10个字符
fgetc()
10;
想要读出写进去的字符,那么就读10次

上面的逻辑是错误的,因为文件当中有一个文件位置指针,写的时候是依次向后移动,读的时候也是,像是一个游标。
读和写一定是从当前位置开始的,上面伪码在写完以后,位置指针最终指向的是第十个位置,然后读的时候,就会从第十一个位置开始。读的内容不知道。

有一个很笨拙地方法:
fp=fopen();
fputc(fp) 10;
fclose(fp);//先关闭
fopen(fp);//然后再一次打开,这个时候文件指针就在文件开始处
fgetc()
10;
以上伪代码可以实现,但是这种方法不实际

fseek();ftell();rewind():

------------------ reposition a stream(重新定位一个文件流)


   #include <stdio.h>
   int fseek(FILE *stream, long offset, int whence);//返回值:如果成功返回0,出错返回非0值
中间的参数是一个偏移量的大小。
第三个参数是一个位置量(从什么位置开始)

   long ftell(FILE *stream);//返回值:若成功,就返回当前文件位置指示,出错就返回-1L,反映出文件位置指针当前位置是哪里

   void rewind(FILE *stream);//相当于:(void) fseek(stream, 0L, SEEK_SET)   功能:将文件指针指在文件的开始处。

SEEK_SET:表示从文件的起始位置开始
SEEK_CUR:表示从文件的当前位置开始
SEEK_END:表示从文件的尾端开始

伪代码可以写成这样:
fp=fopen();
fputc(fp) 10;
想要从文件首开始,可以使用下面的函数
fseek(fp,0,SEEK_SET);
fgetc()
10;

小例子:
函数功能:利用fseek和ftell来计算文件的长度

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
	FILE *fp;
	int count=0;
	if(argc<2)
	{
		fprintf(stderr,"Usage.....\n");
		exit(1);
	}

	fp=fopen(argv[1],"r");
	if(fp==NULL)
	{
		
		perror("fopen():");
		exit(1);
	}

	fseek(fp,0,SEEK_END);
	printf("%ld\n",ftell(fp));
	fclose(fp);
	exit(0);
}

fseek经常用来完成一个空洞文件
空洞文件:文件中全部或是一部分充斥的都是字符0,字符ascall码为零的字符,空字符。
空洞文件用处:当我们下载文件的时候,建立下载任务之后,磁盘上就会产生一个文件,那么这个文件是慢慢涨到所需文件大小,还是一下子就是文件大小?。应该一上来就是原文件的大小。
空洞文件就是刚建立下载任务时的文件。先用空洞文件占下磁盘所需的大小。文件产生之后,就调用fseek,从头部开始直接延伸到文件所需大小,文件里的内容都是ASCLL码为0的字符。然后下载工具将文件切成片,用多线程或是多进程进行每一小块的下载,一般采用多线程的比较多,每个线程把文件当中这块锁住,然后下载任务。别人是不可以利用这个空间的要不然会引起竞争和冲突。

fflush();

---------------- flush a stream

   #include <stdio.h>
int fflush(FILE *stream);

说明:
If the stream argument is NULL, fflush() flushes all open output streams.//如果参数是NULL,那么就会刷新所有的文件流

小例子:

#include <stdio.h>
#include <stdlib.h>


int main()
{
	int i;
	printf("Before while()");
	while(1);
	printf("After while()");
	exit(0);
}

运行结果:
什么都没有,一直在循环。
printf()一系列函数,往标准终端上输出的时候,标准输出是典型的行缓冲模式,碰到换行的时候,来刷新缓冲区或者是一行满的时候刷新缓冲区,所以printf函数加上\n就可以输出
Before while()

还有一种方法就是强制刷新缓冲区

#include <stdio.h>
#include <stdlib.h>


int main()
{
	int i;
	printf("Before while()");
	fflush(stdout);//或者参数写成NULL也行;
	while(1);
	printf("After while()");
	exit(0);
}

缓冲区:
作用:合并系统调用

行缓冲:换行的时候刷新,满了的时候刷新,强制刷新(标准输出,因为是终端设备)

全缓冲:满了的时候刷新、强制刷新(默认,只要不是终端设备)

无缓冲:如stderr,一旦出错,马上输出,需要立即输出

缓冲区是可以改变的,一个文件的缓冲模式是可以改变的

setvbuf(仅了解)

--------------------stream buffering operations

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

The setvbuf() function may be used on any open stream to change its buffer. The mode argument must be one of the following three macros:

  _IONBF unbuffered:无缓冲模式

  _IOLBF line buffered:行缓冲模式

  _IOFBF fully buffered:全缓冲模式

fseek和ftell都用到了long类型
ftell()如果当前的long是32位,就是-(2G-1)~2G-1,但是在文件中只会用到正数,而fssek中间的参数都可以使用,就是说可以正也可以负,一共4G大小,ftell只会达到2G
所以要求当前文件的大小要小于2G
但是现在的文件很多都是比较大的,用的时候就要慎重

可以参考fsseko,和ftello
这两个函数将含有long类型的参数进行替换,变成了off_t类型

fseeko();, ftello();

-------------- seek to or report file position

   #include <stdio.h>
   int fseeko(FILE *stream, off_t offset, int whence);
   off_t ftello(FILE *stream);

On some architectures, both off_t and long are 32-bit types, but defining _FILE_OFFSET_BITS with the value 64 (before including any headerfiles) will turn off_t into a 64-bit type.(在一些环境中,如果编译的时候带有_FILE_OFFSET_BITS=64,off_t一定是64位

但是它的移植性不好,没有C89,C99
CONFORMING TO
POSIX.1-2001, POSIX.1-2008, SUSv2.

getline();

-------------------- delimited string input
getline就原理就是先malloc不够然后再relloc
getline可以取到整个一行的内容:

 #include <stdio.h>
       ssize_t getline(char **lineptr, size_t *n, FILE *stream);
       ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);

getline() reads an entire line from stream, storing the address of thebuffer containing the text into *lineptr.

On success, getline() and getdelim() return the number of characters read, including the delimiter character, but not including the terminating null byte (‘\0’). This value can be used to handle embedded null bytes in the line read.(返回成功读到的字符个数,不包括最后的尾零)

运用:
计算文件内每行的有效字符的个数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char *argv[])
{	
	FILE *fp;
	char *linebuf=NULL;//如果不加,会出现段错
	size_t linesize=0;
	if(argc < 2)
	{
		fprintf(stderr,"Usage~~~~\n");
		exit(1);
	}

	fp=fopen(argv[1],"r");
	if(fp==NULL)
	{
		perror("fopen() fail");
		exit(1);
	}
	while(1)
	{
		if(getline(&linebuf,&linesize,fp)< 0)
		{
			break;
		}
		printf("%d\n",strlen(linebuf));
		
	}

	fclose(fp);
	exit(0);
}

这个程序中的问题在于造成了一定的内存泄漏,getline原理是malloc然后不够用就会再继续扩,有初始化的值,但是没有free操作,linebuf指向的空间没有释放。

临时文件

临时文件:假如一个serve端,用户有一些请求,在工作过程中,会收到客户端发来的很多数据,serve有必要对这些数据存起来,有些文件是永久保存,有的不需要,就会有临时文件。
这个时候就要创建临时文件
临时文件的创建涉及到2个方面
1、如何不冲突的创建临时文件
2、如何及时销毁

相关函数
tmpnam();
---------- create a name for a temporary file

 #include <stdio.h>
       char *tmpnam(char *s);

返回值:
返回一个可用的名字。

如果和另外一个进程,请求的内容都是在同一个路径下
返回的名字会不会都一样?
A进程执行了tmpnam,获得了一个文件名,下一步很有可能就会调用fopen然后选择r和r+以外的权限来创建这个文件。但是若A、B两个进程在并发,A要一个名字创建文件,B同一目录下也要一个名字创建,A要完名字之后还没来得急fopen,B也要来名字创建,结果在当前目录下还没产生分配给A的那个名字的文件,那么这个名字很有可能机会分配给B。
AB把相当于拿到了同一个名字,下面就看谁先执行fopen();谁先执行谁倒霉,假设A先执行了,那么B用的时候就会覆盖或者是改变A的内容

危险原因在于:创建临时文件有两个步骤
不可以一气呵成的创建文件,就是分配完名字立马创建文件
没有办法创建一个非常安全的临时文件

tmpfile
--------------------create a temporary file

   #include <stdio.h>
   FILE *tmpfile(void);

The tmpfile() function opens a unique temporary file in binary read/write (w+b) mode. The file will be automatically deleted when it is closed or the program terminates.(打开一个临时文件以二进制读写形式,打开当前的文件)

这个文件产生了,占用了磁盘的空间,但是在磁盘上看不见
就是ls的时候看不见,但不是隐藏文件,这个时候产生的叫匿名文件。没有名字,就不会产生冲突,我们使用的时候用这个FILE *就可以了,在使用fclose时,文件也会释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值