文件操作函数及文件流详解

这几天着重研究了一下文件操作函数的原理及实现,在这里与大家分享一下————开心

1.文件流

2.几种文件操作函数
(1)  fopen

(2) fclose

(3)fread

(4)fwrite

(5)fgets

(6)fputs


先来看FILE结构体:

#ifndef _FILE_DEFINED
struct _iobuf {
 
        char *_ptr;//文件缓存的当前位置
        int   _cnt;//缓存里可以读取的字节数 
        char *_base;//文件缓存的起始位置
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;//缓存大小
        char *_tmpfname;
         
};
typedef struct _iobuf FILE;



fgetc()

已经给了,注释,在此不再赘述
/***
*fgetc.c - get a character from a stream
*
*       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines fgetc() and getc() - read  a character from a stream
*
*******************************************************************************/
 
#include 
#include 
#include 
#include 
#include 
#include 
 
/***
*int fgetc(stream), getc(stream) - read a character from a stream从流中读数据 
*
*Purpose:
*       reads a character from the given stream从已知流中读数据 
*
*Entry:
*       FILE *stream - stream to read character from进入 
*
*Exit:
*       returns the character read返回读取的值 
*       returns EOF if at end of file or error occurred
*
*Exceptions:
*
*******************************************************************************/
 
int __cdecl fgetc (REG1 FILE *stream)
{
 
        int retval;
 
        _ASSERTE(stream != NULL);
 
        _lock_str(stream);//锁定
        retval = _getc_lk(stream);//从流中读取 
        _unlock_str(stream);//取消锁定 
 
        return(retval);//返回值    :注意 是int类型!! 
 
}
 
#undef getc
 
int __cdecl getc (
        FILE *stream
        )
{
 
        return fgetc(stream);
 
}

fgets()

/***
*fgets.c - get string from a file
*
*       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines fgets() - read a string from a file
*
*******************************************************************************/
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
/***
*char *fgets(string, count, stream) - input string from a stream
*
*Purpose:
*       get a string, up to count-1 chars or '\n', whichever comes first,
*       append '\0' and put the whole thing into string. the '\n' IS included
*       in the string. if count<=1 no input is requested. if EOF is found
*       immediately, return NULL. if EOF found after chars read, let EOF
*       finish the string as '\n' would.
*
*Entry:
*       char *string - pointer to place to store string
*       int count - max characters to place at string (include \0)
*       FILE *stream - stream to read from三个参数 
*
*Exit:
*       returns string with text read from file in it.
*       if count <= 0 return NULL
*       if count == 1 put null string in string
*       returns NULL if error or end-of-file found immediately
*
*Exceptions:
*
*******************************************************************************/
 
#ifdef _UNICODE
wchar_t * __cdecl fgetws (
#else  /* _UNICODE */
char * __cdecl fgets (
#endif  /* _UNICODE */
        _TSCHAR *string,//用于储存的数组 
        int count,//个数 
        FILE *str
        )
{
 
    REG1 FILE *stream;
    REG2 _TSCHAR *pointer = string;
    _TSCHAR *retval = string;
    int ch;
 
    _ASSERTE(string != NULL);
    _ASSERTE(str != NULL);
 
    if (count <= 0)
        return(NULL);
    /* Init stream pointer */
    stream = str; 
 
    _lock_str(stream);//锁定流 
 
    while (--count)
    {
 
	#ifdef _UNICODE
            if ((ch = _getwc_lk(stream)) == WEOF)
	#else  /* _UNICODE */
            if ((ch = _getc_lk(stream)) == EOF)//从流中得到一个字母 ,如果到末尾跳出
	#endif  /* _UNICODE */
            {
                if (pointer == string) 
				{
                    retval=NULL;
                    goto done;                 
				}
                break;     
			}
 
            if ((*pointer++ = (_TSCHAR)ch) == _T('\n'))//把流中的值付给数组,当遇到\n时跳出 
                break;
         
	}
 
    *pointer = _T('\0');//最后加上\0 
 
/* Common return */
done:
        _unlock_str(stream);//解锁流 ,可能与flag有关
        return(retval);
}







fread()

/***
*fread.c - read from a stream
*
*       Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       Read from the specified stream into the user's buffer.
*
*******************************************************************************/
 
#include 
#include 
#include 
#include 
#include 
#include 
 
/***
*size_t fread(void *buffer, size_t size, size_t count, FILE *stream) -
*       read from specified stream into the specified buffer.
*
*Purpose:
*       Read 'count' items of size 'size' from the specified stream into
*       the specified buffer. Return when 'count' items have been read in
*       or no more items can be read from the stream.
*
*Entry:
*       buffer  - pointer to user's buffer
*       size    - size of the item to read in
*       count   - number of items to read
*       stream  - stream to read from
*
*Exit:
*       Returns the number of (whole) items that were read into the buffer.
*       This may be less than 'count' if an error or eof occurred. In this
*       case, ferror() or feof() should be used to distinguish between the
*       two conditions.
*
*Notes:
*       fread will attempt to buffer the stream (side effect of the _filbuf
*       call) if necessary.
*
*       No more than 0xFFFE bytes may be read in at a time by a call to
*       read(). Further, read() does not handle huge buffers. Therefore,
*       in large data models, the read request is broken down into chunks
*       that do not violate these considerations. Each of these chunks is
*       processed much like an fread() call in a small data model (by a
*       call to _nfread()).
*
*       MTHREAD/DLL - Handled in three layers. fread() handles the locking
*       and DS saving/loading/restoring (if required) and calls _fread_lk()
*       to do the work. _fread_lk() is the same as the single-thread,
*       large data model version of fread(). It breaks up the read request
*       into digestible chunks and calls _nfread() to do the actual work.
*
*       386/MTHREAD/DLL - Handled in just the two layers since it is small
*       data model. The outer layer, fread(), takes care of the stream locking
*       and calls _fread_lk() to do the actual work. _fread_lk() is the same
*       as the single-thread version of fread().
*
*******************************************************************************/
 
 
#ifdef _MT
/* define locking/unlocking version */
size_t __cdecl fread (
        void *buffer,
        size_t size,
        size_t count,
        FILE *stream
        )
{
 
        size_t retval;
 
        _lock_str(stream);                                /* lock stream */
        retval = _fread_lk(buffer, size, count, stream);  /* do the read */
        _unlock_str(stream);                              /* unlock stream */
        return retval;
 
}
#endif  /* _MT */
 
/* define the normal version */
#ifdef _MT
size_t __cdecl _fread_lk (
#else  /* _MT */
size_t __cdecl fread (
#endif  /* _MT */
        void *buffer,
        size_t size,
        size_t num,
        FILE *stream
        )
{
 
        char *data;                     /* point to where should be read next */
        unsigned total;                 /* total bytes to read */
        unsigned count;                 /* num bytes left to read */
        unsigned bufsize;               /* size of stream buffer */
        unsigned nbytes;                /* how much to read now */
        unsigned nread;                 /* how much we did read */
        int c;                          /* a temp char */
 
        /* initialize local vars */
        data = buffer;
 
        if ( (count = total = size * num) == 0 )
                return 0;
 
        if (anybuf(stream))
                /* already has buffer, use its size */
                bufsize = stream->_bufsiz;
        else
#if defined (_M_M68K) || defined (_M_MPPC)
                /* assume will get BUFSIZ buffer */
                bufsize = BUFSIZ;
#else  /* defined (_M_M68K) || defined (_M_MPPC) */
                /* assume will get _INTERNAL_BUFSIZ buffer */
                bufsize = _INTERNAL_BUFSIZ;
#endif  /* defined (_M_M68K) || defined (_M_MPPC) */
 
        /* here is the main loop -- we go through here until we're done */
        while (count != 0) {
 
                /* if the buffer exists and has characters, copy them to user
                   buffer */
                if (anybuf(stream) && stream->_cnt != 0) {
 
                        /* how much do we want? */
                        nbytes = (count < (unsigned)stream->_cnt) ? count : stream->_cnt;
                        memcpy(data, stream->_ptr, nbytes);
 
                        /* update stream and amt of data read */
                        count -= nbytes;
                        stream->_cnt -= nbytes;
                        stream->_ptr += nbytes;
                        data += nbytes;
                 
}
                else if (count >= bufsize) {
 
                        /* If we have more than bufsize chars to read, get data
                           by calling read with an integral number of bufsiz
                           blocks.  Note that if the stream is text mode, read
                           will return less chars than we ordered. */
 
                        /* calc chars to read -- (count/bufsize) * bufsize */
                        nbytes = ( bufsize ? (count - count % bufsize) :
                                   count );
 
                        nread = _read(_fileno(stream), data, nbytes);
                        if (nread == 0) {
 
                                /* end of file -- out of here */
                                stream->_flag |= _IOEOF;
                                return (total - count) / size;
                         
}
                        else if (nread == (unsigned)-1) {
 
                                /* error -- out of here */
                                stream->_flag |= _IOERR;
                                return (total - count) / size;
                         
}
 
                        /* update count and data to reflect read */
                        count -= nread;
                        data += nread;
                 
}
                else {
 
                        /* less than bufsize chars to read, so call _filbuf to
                           fill buffer */
                        if ((c = _filbuf(stream)) == EOF) {
 
                                /* error or eof, stream flags set by _filbuf */
                                return (total - count) / size;
                         
}
 
                        /* _filbuf returned a char -- store it */
                        *data++ = (char) c;
                        --count;
 
                        /* update buffer size */
                        bufsize = stream->_bufsiz;
                 
}
         
}
 
        /* we finished successfully, so just return num */
        return num;
 
}



问题解析:fgets与fputs一块用时出现的问题:

来看问题:
怎么实现向test.txt文件中输入10行:
welcome to xiyoulinux


#include
int main(int argc, char *argv[])
{
int i = 0;
FILE *fp = fopen("temp.txt", "w+");
char string[] = "WELCOME TO XIYOULINUX\n";
char secondstring[] = "welcome to xiyoulinux\n";
char temp[sizeof(string)];
for(i = 0; i<10; i++)
fputs(string, fp);
fseek(fp, 0, SEEK_SET);
while((fgets(temp, sizeof(string), fp)) != NULL)
{
	fseek(fp, -(long)sizeof(temp), 1);
	fputs(secondstring, fp);
}
fclose(fp);
return 0;
}


问题解决过程:

我把程序运行了一下,发现是个死循环,而且文件中的数据根本没法实现所有都会替换成     小写的    于是我去查阅资料,发现了许多东西,接下来我一一解释


1.fputs()函数在连续读取时,不会每次都把缓冲区内容放入文件中,而是等到调用另一个不同的函数时,才读入文件中。

证明:

#include
#include
#include 
#include
void  printinfo(FILE *fp);
int main(int argc, char *argv[])
{
	char ch;
	int i = 0;
	FILE *fp = fopen("temp.txt", "w+");
	char string[] = "WELCOME TO XIYOULINUX\n";
	char secondstring[] = "welcome to xiyoulinux\n";
	char temp[sizeof(string)];
	printf("%d\n\n",sizeof(string));
	for(i = 0; i<10; i++)
	{
		
		printf("\n\n\n\*****%d*******before_fputs:*******************",i);
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("end\n");
		printf("*******************end*************************");
		fputs(string, fp);
		printf("\n************after_fputs:*******************");
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("");
		printf("all=%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
		printf("****************end****************************\n\n\n\n");
		getch();//停止,看一下文件 
			
	}
	printf("before_fseek:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
	fseek(fp, 0, SEEK_SET);
	printf("after_fseek||before_fgets:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
	printf("\n\n");
	printinfo(fp);
	printf("\n");
	getch();//停止,查看文件
	
	while((fgets(temp,sizeof(string),fp)) != NULL)
	{
		printf("\n*******************************************\n");
		printf("after_fgets||before_fseek:\n");
		printinfo(fp);
		printf("1:\n%d\n",fp->_cnt/(sizeof(string)-1));
		
		printf("%p\n",fp->_base);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+50;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
				
		fseek(fp, -(long)sizeof(temp), 1);
		
		printf("\n*******************************************\n");	
		printf("after_fseek||before_fputs:\n");
		printinfo(fp);
		printf("\n");
		printf("2:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));	
		printf("\n*******************************************\n");
		printf("\n\n");
		
		fputs(secondstring, fp);
		printf("\n*******************************************\n");
		printf("after_fputs||before_fgets:\n");
		printinfo(fp);
		printf("\n");
		printf("3:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
	//	fflush(fp);
	//	fseek(fp, 0, 1);
	//	fp->_flag=136;
	//	fp->_ptr=fp->_base;
	//	fp->_cnt=0;
		
		printf("\n****************after_fseek***************************\n");
		printinfo(fp);
		printf("\n");
		printf("4:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
		printf("\n\n\n");
		
		getch();
		//break;
	}
	fclose(fp);
	printf("after:fclose:\n");
	printinfo(fp);
	printf("\n");
	printf("\n\n");
	return 0;
}

void  printinfo(FILE *fp)
{
	printf("fp->_base:%p\n",fp->_base);
	printf("fp->_bufsiz:%d\n",fp->_bufsiz);
	printf("fp->_charbuf:%d\n",fp->_charbuf);
	printf("fp->_cnt:%d\n",fp->_cnt);
	printf("fp->_file:%d\n",fp->_file);
	printf("fp->_flag:%d\n",fp->_flag);
	printf("fp->_ptr=%p\n",fp->_ptr);
	printf("fp->_tmpfname:%p\n",fp->_tmpfname);
	
		
} 

结果:


文件操作函数及文件流详解_第1张图片

第一次循环


文件操作函数及文件流详解_第2张图片


最后一次循环
这十次循环时,文件内容都没有录入

文件操作函数及文件流详解_第3张图片



文件操作函数及文件流详解_第4张图片


当运行到fseek()函数时,发现了文件内容发生了变化。



文件操作函数及文件流详解_第5张图片



这里验证了假设一




2.fseek(fp, 0, SEEK_SET);函数把数据流当前指针移动到”头指针“(即流的首地址),然后把流的状态改变。


验证:

函数执行前:

文件操作函数及文件流详解_第6张图片

函数执行后:



文件操作函数及文件流详解_第7张图片



可以看出_ptr指针已经复原

假设得到验证。

所以所谓的文件内部指针可以理解为流中的_ptr指针(后来经过实验,这句结论是错误的):其实文件内部是有指针的,而且可以通过ftell()函数得到,
两者是分开的,不是一个概念




3.关于结构体内部标志_flag的理解与分析:

(1)_flag=128     流未初始化阶段,程序没有为流分配空间,所有没有首地址,
未初始化的流:

文件操作函数及文件流详解_第8张图片


假设可以通过以上得到验证



(2)  _flag=138     流的接收外部输入的阶段,输入的数据将被录入到文件


验证:fgets()函数为向流中输入的函数,该函数执行后_flag应为138


文件操作函数及文件流详解_第9张图片


可以看出,流已经初始化,_flag=3,而且此时的_cnt表示为还可以向流中输入的数量,既流的剩余空间




(3)_flag=137   文件的数据读取到流,流向外界输出

验证:fputs()   该函数把流中的数据读入到一个字符串当中

当执行fputs()函数后:

文件操作函数及文件流详解_第10张图片


可以看出,此时文件内容已经读取到流中,而且此时的_flag为137,而且此时的的_cnt的大小表示现在流中可读取的数据。
但是,根据我的研究,这只是个”假象“,事实上,当fgets函数把流中的内容放到一个数组当中时,只是把_ptr指针向后移动了相应位置,_cnt减去了相应大小,而流中的内容是不变的,只是不受”保护”(锁定)了,所有在对应位置上读取,还是可以都到原数据的:
以下是证明:


这个是从_base开始读取的

文件操作函数及文件流详解_第11张图片




这个是从_ptr开始读取的:

文件操作函数及文件流详解_第12张图片

对比两幅图,可以看出,
第一幅中从_base开始输出,则其中的内容仍然存在,一共10行,说明其原始数据还是存在的
第二幅图:从_str指针开始输出,一共9行,说明_str指针向后移动了一行


(4)  _flag=136     已初始化,但不属于读取状态,也不属于写入状态,是一种中间态,这种中间态十分重要
在从读取状态转化为写入状态时,或者是从写入状态转化为读取状态时,都要经过这个中间态, 否则会出现错误(我会在后面解释)

这个中间态我目前知道的可以用    :(1) fflush()函数实现   (2)fseek()函数实现

验证:


文件操作函数及文件流详解_第13张图片


fseek()函数执行后_flag=136 ,_str指到_base,_cnt归零表示可利用的数据为0
内容不变



文件操作函数及文件流详解_第14张图片



利用fflush()函数后,其效果跟fseek()一样,_flag=136 ,_str指到_base,_cnt归零表示可利用的数据为0
内容不变,除此之外,该函数还把流中的数据储存到文件中。

(5)   _flag=0
当执行fclose文件流关闭,流空间释放,_flag置0,表示流已经关闭

执行fcolse()函数后:

文件操作函数及文件流详解_第15张图片
 

可以看出,flag为0    流已经清空



总结一下:_flag:

(1)_flag=128     流未初始化阶段,程序没有为流分配空间。
相应函数:fopen()

(2)  _flag=138     流的接收外部输入的阶段,输入的数据将被录入到文件.
相应函数:fputs()   


(3)_flag=137   文件的数据读取到流,流向外界输出
相应函数:fputs


(4)  _flag=136     已初始化,但不属于读取状态,也不属于写入状态,是一种中间态,这种中间态十分重要
在从读取状态转化为写入状态时,或者是从写入状态转化为读取状态时,都要经过这个中间态, 否则会出现错误(我会在后面解释)
相应函数:fflush()   fseek()

5)   _flag=0
当执行fclose文件流关闭,流空间释放,_flag置0,表示流已经关闭


4.函数何时把流中的数据放入到文件中

我根据一些实验,针对于这个程序,我做了一些调试,结果证明了,在fputs()和fgets()函数之间,其是不向文件中输入数据的。

实验代码如下:


#include
#include
#include 
#include
void  printinfo(FILE *fp);
int main(int argc, char *argv[])
{
	char ch;
	int i = 0;
	FILE *fp = fopen("temp.txt", "w+");
	char string[] = "WELCOME TO XIYOULINUX\n";
	char secondstring[] = "welcome to xiyoulinux\n";
	char temp[sizeof(string)];
	printf("%d\n\n",sizeof(string));
	for(i = 0; i<10; i++)
	{
		
		printf("\n\n\n\*****%d*******before_fputs:*******************",i);
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("end\n");
		printf("*******************end*************************");
		fputs(string, fp);
		printf("\n************after_fputs:*******************");
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("");
		printf("all=%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
		printf("****************end****************************\n\n\n\n");
		//getch(); 
			
	}
	printf("before_fseek:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
	fseek(fp, 0, SEEK_SET);
	printf("after_fseek||before_fgets:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
	printf("\n\n");
	printinfo(fp);
	printf("\n");
//	getch();
	
	while((fgets(temp,sizeof(string),fp)) != NULL)
	{
		printf("cur=fgets\n");
		getch(); 
		printf("\n*******************************************\n");
		printf("after_fgets||before_fseek:\n");
		printinfo(fp);
		printf("1:\n%d\n",fp->_cnt/(sizeof(string)-1));
		
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+50;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+50;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
			
		fseek(fp, -(long)sizeof(temp), 1);
		printf("cur=fseek\n");
		getch();
		
		printf("\n*******************************************\n");	
		printf("after_fseek||before_fputs:\n");
		printinfo(fp);
		printf("\n");
		printf("2:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));	
		printf("\n*******************************************\n");
		printf("\n\n");
	
		fputs(secondstring, fp);
		printf("cur=fputs\n");
		getch(); 
		printf("\n*******************************************\n");
		printf("after_fputs||before_fgets:\n");
		printinfo(fp);
		printf("\n");
		printf("3:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
	//	fflush(fp);
		//fputs(secondstring, fp);
		//getch();
		//fseek(fp, 0, 1);
	//	rewind(fp);
	//	fp->_flag=136;
	//	fp->_ptr=fp->_base;
	//	fp->_cnt=0;
		 
		/*printf("\n****************after_fseek***************************\n");
		printinfo(fp);
		printf("\n");
		printf("4:%d\n",fp->_cnt/(sizeof(string)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
		printf("\n\n\n");
		*/ 
	
		//break;
	}
	fclose(fp);
	printf("after:fclose:\n");
	printinfo(fp);
	printf("\n");
	printf("\n\n");
	return 0;
}

void  printinfo(FILE *fp)
{
	printf("fp->_base:%p\n",fp->_base);
	printf("fp->_bufsiz:%d\n",fp->_bufsiz);
	printf("fp->_charbuf:%d\n",fp->_charbuf);
	printf("fp->_cnt:%d\n",fp->_cnt);
	printf("fp->_file:%d\n",fp->_file);
	printf("fp->_flag:%d\n",fp->_flag);
	printf("fp->_ptr=%p\n",fp->_ptr);
	printf("fp->_tmpfname:%p\n",fp->_tmpfname);
	
		
} 


来看结果;


当运行fputs()后暂停

文件操作函数及文件流详解_第16张图片



文件结果:

文件操作函数及文件流详解_第17张图片



当运行fgets()函数时:

文件操作函数及文件流详解_第18张图片

文件结果:

文件操作函数及文件流详解_第19张图片




可以看出,文件没发生变化。


运行了fseek()

文件操作函数及文件流详解_第20张图片

文件结果:


文件操作函数及文件流详解_第21张图片


文件内容发生了变化
说明了原来的预测!





5.函数fputs(string,pf)读取过程:
其根据_ptr指针位置,从_ptr位置开始,向缓冲区中写入string字符数组,并覆盖原来内容。


下面来验证:



先贴上验证代码:

#include
#include
#include 
#include
void  printinfo(FILE *fp);
int main(int argc, char *argv[])
{
	char ch;
	int i = 0,flag=0;
	FILE *fp = fopen("temp.txt", "w+");
	char string1[] = "1WELCOME TO XIYOULINUX\n";
	char string2[] = "2WELCOME TO XIYOULINUX\n";
	char string3[] = "3WELCOME TO XIYOULINUX\n";
	char string4[] = "4WELCOME TO XIYOULINUX\n";
	char string5[] = "5WELCOME TO XIYOULINUX\n";
	char string6[] = "6WELCOME TO XIYOULINUX\n";
	char string7[] = "7WELCOME TO XIYOULINUX\n";
	char string8[] = "8WELCOME TO XIYOULINUX\n";
	char string9[] = "9WELCOME TO XIYOULINUX\n";
	char string10[] = "0WELCOME TO XIYOULINUX\n";
	char secondstring[] = "0welcome to xiyoulinux\n";
	char secondstring1[] = "1welcome to xiyoulinux\n";
	char secondstring2[] = "2welcome to xiyoulinux\n";
	char secondstring3[] = "3welcome to xiyoulinux\n";
	char secondstring4[] = "4welcome to xiyoulinux\n";
	char temp[sizeof(string1)];

	printf("\n\n\n*****%d*******before_fputs:*******************",i);
	printf("\n");
	printinfo(fp);
	printf("\n");
	printf("end\n");
	printf("*******************end*************************");


	fputs(string1, fp);
	fputs(string2, fp);
	fputs(string3, fp);
	fputs(string4, fp);
	fputs(string5, fp);
	fputs(string6, fp);
	fputs(string7, fp);
	fputs(string8, fp);
	fputs(string9, fp);
	fputs(string10, fp);
	
	/*for(i = 0; i<10; i++)
	{
		
		printf("\n\n\n\*****%d*******before_fputs:*******************",i);
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("end\n");
		printf("*******************end*************************");
		
		fputs(string, fp);
		
		printf("\n************after_fputs:*******************");
		printf("\n");
		printinfo(fp);
		printf("\n");
		printf("");
		printf("all=%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string)-1));
		printf("****************end****************************\n\n\n\n");
		//getch(); 
			
	}*/
	printf("\n************after_fputs:*******************");
	printf("\n");
	printinfo(fp);
	printf("\n");
	printf("all=%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string1)-1));
	printf("****************end****************************\n\n\n\n");
	printf("before_fseek:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string1)-1));

	fseek(fp, 0, SEEK_SET);

	printf("after_fseek||before_fgets:%d\n",(fp->_bufsiz-fp->_cnt)/(sizeof(string1)-1));
	printf("\n\n");
	printinfo(fp);
	printf("\n");
//	getch();
	
	while((fgets(temp,sizeof(string1),fp)) != NULL)
	{
		flag++;
		printf("\n*******************************************\n");
		printf("file:%ld\n",ftell(fp));
		printf("after_fgets||before_fseek:\n");
		printinfo(fp);
		printf("1:%d\n",fp->_cnt/(sizeof(string1)-1));
		
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+50;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+50;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
		printf("cur=fgets\n");
		getch();
		
		
	//	fp->_ptr=fp->_base; 
		//fseek(fp, -(long)sizeof(temp), 1);
		
		
		/*printf("\n*******************************************\n");	
		printf("%ld\n",ftell(fp));
		printf("after_fseek||before_fputs:\n");
		printinfo(fp);
		printf("\n");
		printf("2:%d\n",fp->_cnt/(sizeof(string1)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));	
		printf("\n*******************************************\n");
		printf("\n\n");
		printf("cur=fseek\n");
		getch();*/
		switch(flag)
		{
			case 1:
				fputs(secondstring, fp);
				break;
			case 2:
				fputs(secondstring1, fp);
				break;
			case 3:
				fputs(secondstring2, fp);
				break;
			case 4:
				fputs(secondstring3, fp);
				break;
			case 5:
				fputs(secondstring4, fp);
				break;
		}
		
		
		printf("\n*******************************************\n");
		printf("%ld\n",ftell(fp));
		printf("after_fputs||before_fgets:\n");
		printinfo(fp);
		printf("\n");
		printf("3:%d\n",fp->_cnt/(sizeof(string1)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt;i++)  printf("%c",*(fp->_ptr+i));
		
		printf("\n*******************************************\n");
		printf("cur=fputs\n");
		getch(); 
		//fflush(fp);
		//fputs(secondstring, fp);
		//getch();
	//	fseek(fp, -sizeof(string1), 1);
	//	rewind(fp);
	//	fp->_flag=136;
	//	fp->_ptr=fp->_base;
	//	fp->_cnt=0;
		 
	/*	printf("\n****************after_fseek(fp, -sizeof(string1), 1)***************************\n");
		printf("%ld\n",ftell(fp));
		printinfo(fp);
		printf("\n");
		printf("4:%d\n",fp->_cnt/(sizeof(string1)-1));
		printf("%p\n",fp->_base);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_base+i));
		printf("\n");
		printf("%p\n",fp->_ptr);
		for(i=0;i_cnt+400;i++)  printf("%c",*(fp->_ptr+i));
		printf("\n*******************************************\n");
		printf("\n");
		printf("fseek(fp, -sizeof(string1), 1)");
		getch();
	*/
		//break;
	}
	fclose(fp);
	printf("after:fclose:\n");
	printinfo(fp);
	printf("\n");
	printf("\n\n");
	return 0;
}

void  printinfo(FILE *fp)
{
	printf("fp->_base:%p\n",fp->_base);
	printf("fp->_bufsiz:%d\n",fp->_bufsiz);
	printf("fp->_charbuf:%d\n",fp->_charbuf);
	printf("fp->_cnt:%d\n",fp->_cnt);
	printf("fp->_file:%d\n",fp->_file);
	printf("fp->_flag:%d\n",fp->_flag);
	printf("fp->_ptr=%p\n",fp->_ptr);
	printf("fp->_tmpfname:%p\n",fp->_tmpfname);
} 




再来截图:

第一次fgets()读文件,_ptr向后移动一个"单位"

文件操作函数及文件流详解_第22张图片

fputs(),其在_ptr向后覆盖了原来缓冲区内容,,而且_ptr向后移动了一个"单元"

文件操作函数及文件流详解_第23张图片

fgets(temp,fp)把_ptr后单位长度数据放入到数组中,而且_ptr向后移动一个单位

文件操作函数及文件流详解_第24张图片

fputs()从_str向后覆盖了一个"单位"的数据

文件操作函数及文件流详解_第25张图片


经过以上验证了,结论得到了验证。








好了,分析了那么多,现在开始分析问题的本质,
经过我两天的研究,我发现了问题的根本所在,其原因就是fseek()上,
当运行代码:fseek(fp, -(long)sizeof(temp), 1);时,其中的_ptr并没有像我们想象的那样
向前跳一个单位长度,而是跳了两个单位长度

函数执行前:

文件操作函数及文件流详解_第26张图片


函数执行后:


文件操作函数及文件流详解_第27张图片


所以就找到了问题根本所在

那么,就想办法解释,探索:
经过探索,我发现只要是执行fseek函数,无论文件内部函数怎么移动,_base的值就会被赋给_ptr

进一步验证:,我把文件内部指针移动到最末尾,其_ptr值还是等于_base

利用:fseek(fp, 0, SEEK_END);


文件操作函数及文件流详解_第28张图片


所以就明白了其原因,,










你可能感兴趣的:(文件操作函数,C基础)