fread-C语言是怎样读取文件的

原文:http://blog.csdn.net/xiaofengsheng/article/details/5370917

为了效率的考虑,不至于频繁调用系统函数和访问IO设备,MSVC CRT的fread采用缓冲设计.

 

C语言提供的关于缓冲的函数有:

[cpp]  view plain copy
  1. int flush(FILE* stream);  
  2.   
  3. int setvbuf(FILE* stream, char* buf, int mode, size_t size);  
  4.   
  5. /* 
  6. 缓冲模式mode有: 
  7. 1. 无缓冲模式 _IONBF 
  8. 2. 行缓冲模式 _IOLBF 每收到一个换行符(/n或/r/n), 就将缓冲flush掉 
  9. 3. 全缓冲模式 _IOFBF 仅当缓冲满时才进行flush 
  10. */  
  11.   
  12. void setbuf(FILE* stream, char* buf); 等价于 (void)setvbuf(stream, buf, _IOBBF, BUFSIZ);  

 

fread的调用过程大概是:

fread -> fread_s(增加缓冲溢出保护, 加锁) -> _fread_nolock_s(循环读取,缓冲) -> _read(换行符转换) -> ReadFile(读取文件)

 

加注释的FILE结构如下:

[cpp]  view plain copy
  1. struct _iobuf {  
  2.     char *_ptr;   
  3.     int   _cnt; //剩余未读的字节数  
  4.     char *_base; //文件的缓冲基址  
  5.     int   _flag; //打开文件的属性  
  6.     int   _file; //打开文件的编号  
  7.     int   _charbuf;  
  8.     int   _bufsiz; //文件的缓冲的总的大小  
  9.     char *_tmpfname;  
  10. };  
  11. typedef struct _iobuf FILE;  

 

核心函数_fread_nolock_s(循环读取,缓冲)如下:

[cpp]  view plain copy
  1. size_t __cdecl _fread_nolock_s(  
  2.                                void *buffer,  
  3.                                size_t bufferSize,  
  4.                                size_t elementSize,  
  5.                                size_t num,  
  6.                                FILE *stream  
  7.                                )  
  8. {  
  9.     char *data;                     /* point inside the destination buffer to where we need to copy the read chars */当前放进字节的尾部  
  10.         size_t dataSize;                /* space left in the destionation buffer (in bytes) //buffer中剩余字节数*/  
  11.         size_t total;                   /* total bytes to read //总共要读的字节数*/  
  12.         size_t count;                   /* num bytes left to read //剩下要读的字节数*/  
  13.         unsigned streambufsize;         /* size of stream buffer */  
  14.     unsigned nbytes;                /* how much to read now */  
  15.     unsigned nread;                 /* how much we did read */  
  16.     int c;                          /* a temp char */  
  17.   
  18.     /* initialize local vars */  
  19.     data = buffer;  
  20.     dataSize = bufferSize;  
  21.   
  22.     if (elementSize == 0 || num == 0)  
  23.     {  
  24.         return 0;  
  25.     }  
  26.   
  27.     /* validation */  
  28.     _VALIDATE_RETURN((buffer != NULL), EINVAL, 0);  
  29.     if (stream == NULL || num > (SIZE_MAX / elementSize))  
  30.     {  
  31.         if (bufferSize != SIZE_MAX)  
  32.         {  
  33.             memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);  
  34.         }  
  35.   
  36.         _VALIDATE_RETURN((stream != NULL), EINVAL, 0);  
  37.         _VALIDATE_RETURN(num <= (SIZE_MAX / elementSize), EINVAL, 0);  
  38.     }  
  39.   
  40.   
  41.     count = total = elementSize * num;  
  42.   
  43.     if (anybuf(stream))  
  44.     {  
  45.         /* already has buffer, use its size */  
  46.         streambufsize = stream->_bufsiz;  
  47.     }  
  48.     else  
  49.     {  
  50.         /* assume will get _INTERNAL_BUFSIZ buffer */  
  51.         streambufsize = _INTERNAL_BUFSIZ;  
  52.     }  
  53.   
  54.     /* here is the main loop -- we go through here until we're done */  
  55.     while (count != 0) {  
  56.         /* if the buffer exists and has characters, copy them to user 
  57.         buffer */  
  58.         if (anybuf(stream) && stream->_cnt != 0)  
  59.         {  
  60.             if(stream->_cnt < 0)  
  61.             {  
  62.                 _ASSERTE(("Inconsistent Stream Count. Flush between consecutive read and write", stream->_cnt >= 0));  
  63.                 stream->_flag |= _IOERR;  
  64.                 return (total - count) / elementSize;  
  65.             }  
  66.   
  67.             /* how much do we want? */  
  68.             nbytes = (count < (size_t)stream->_cnt) ? (unsigned)count : stream->_cnt;  
  69.             if (nbytes > dataSize)  
  70.             {  
  71.                 if (bufferSize != SIZE_MAX)  
  72.                 {  
  73.                     memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);  
  74.                 }  
  75.                 _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)  
  76.             }  
  77.             memcpy_s(data, dataSize, stream->_ptr, nbytes);  
  78.   
  79.             /* update stream and amt of data read */  
  80.             count -= nbytes;  
  81.             stream->_cnt -= nbytes;  
  82.             stream->_ptr += nbytes;  
  83.             data += nbytes;  
  84.             dataSize -= nbytes;  
  85.         }  
  86.         else if (count >= streambufsize)  
  87.         {  
  88.             /* If we have more than streambufsize chars to read, get data 
  89.             by calling read with an integral number of bufsiz 
  90.             blocks.  Note that if the stream is text mode, read 
  91.             will return less chars than we ordered. */  
  92.   
  93.             if (streambufsize)  
  94.             {  
  95.                 /* In 64bit apps size_t is bigger than unsigned 
  96.                 * (which is 32bit even in 64 bit machines), so 
  97.                 * we need to split the read into INT_MAX chunks 
  98.                 * since _read() only support up to _signed_ int 
  99.                 * (even though the in parameter is unsigned). 
  100.                 */  
  101.                 if (count > INT_MAX)  
  102.                 {  
  103.                     /* calc chars to read -- the largest multiple of streambufsize 
  104.                     * smaller then INT_MAX 
  105.                     */  
  106.                     nbytes = (unsigned)(INT_MAX - INT_MAX % streambufsize);  
  107.                 }  
  108.                 else  
  109.                 {  
  110.                     /* calc chars to read -- (count/streambufsize) * streambufsize */  
  111.                     nbytes = (unsigned)(count - count % streambufsize);  
  112.                 }  
  113.             }  
  114.             else  
  115.             {  
  116.                 nbytes = (count > INT_MAX)?(unsigned)INT_MAX: (unsigned)count;  
  117.             }  
  118.   
  119.             if (nbytes > dataSize)  
  120.             {  
  121.                 if (bufferSize != SIZE_MAX)  
  122.                 {  
  123.                     memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);  
  124.                 }  
  125.                 _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)  
  126.             }  
  127.   
  128.             nread = _read(_fileno(stream), data, nbytes);  
  129.             if (nread == 0) {  
  130.                 /* end of file -- out of here */  
  131.                 stream->_flag |= _IOEOF;  
  132.                 return (total - count) / elementSize;  
  133.             }  
  134.             else if (nread == (unsigned)-1) {  
  135.                 /* error -- out of here */  
  136.                 stream->_flag |= _IOERR;  
  137.                 return (total - count) / elementSize;  
  138.             }  
  139.   
  140.             /* update count and data to reflect read */  
  141.             count -= nread;  
  142.             data += nread;  
  143.             dataSize -= nread;  
  144.         }  
  145.         else  
  146.         {  
  147.             /* less than streambufsize chars to read, so call _filbuf to 
  148.             fill buffer */  
  149.             if ((c = _filbuf(stream)) == EOF) {  
  150.                 /* error or eof, stream flags set by _filbuf */  
  151.                 return (total - count) / elementSize;  
  152.             }  
  153.   
  154.             /* _filbuf returned a char -- store it */  
  155.             if (dataSize == 0)  
  156.             {  
  157.                 if (bufferSize != SIZE_MAX)  
  158.                 {  
  159.                     memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);  
  160.                 }  
  161.                 _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)  
  162.             }  
  163.             *data++ = (char) c;  
  164.             --count;  
  165.             --dataSize;  
  166.   
  167.             /* update buffer size */  
  168.             streambufsize = stream->_bufsiz;  
  169.         }  
  170.     }  
  171.   
  172.     /* we finished successfully, so just return num */  
  173.     return num;  
  174. }  

 

其中,

[cpp]  view plain copy
  1. int __cdecl _filwbuf (  
  2.         FILE *str  
  3.         )  
  4.   
  5. #endif  /* _UNICODE */  
  6.   
  7. {  
  8.   
  9.         REG1 FILE *stream=NULL;  
  10.   
  11.                 /* In safecrt, we assume we always have a buffer */  
  12.         _VALIDATE_RETURN(str != NULL, EINVAL, _TEOF);  
  13.   
  14.         /* Init pointer to _iob2 entry. */  
  15.         stream = str;  
  16.   
  17.         if (!inuse(stream) || stream->_flag & _IOSTRG)  
  18.                 return(_TEOF);  
  19.   
  20.         if (stream->_flag & _IOWRT) {  
  21.                 stream->_flag |= _IOERR;  
  22.                 return(_TEOF);  
  23.         }  
  24.   
  25.         stream->_flag |= _IOREAD;  
  26.   
  27.         /* Get a buffer, if necessary. */  
  28.   
  29.         if (!anybuf(stream))  
  30.         {  
  31. #ifndef _SAFECRT_IMPL  
  32.             _getbuf(stream);  
  33. #else  /* _SAFECRT_IMPL */  
  34.             /* In safecrt, we assume we always have a buffer */  
  35.             _VALIDATE_RETURN(FALSE, EINVAL, _TEOF);  
  36. #endif  /* _SAFECRT_IMPL */  
  37.         }  
  38.         else  
  39.         {  
  40.             stream->_ptr = stream->_base;  
  41.         }  
  42.   
  43.         stream->_cnt = _read(_fileno(stream), stream->_base, stream->_bufsiz);  
  44.   
  45. #ifndef _UNICODE  
  46.         if ((stream->_cnt == 0) || (stream->_cnt == -1)) {  
  47. #else  /* _UNICODE */  
  48.         if ((stream->_cnt == 0) || (stream->_cnt == 1) || stream->_cnt == -1) {  
  49. #endif  /* _UNICODE */  
  50.                 stream->_flag |= stream->_cnt ? _IOERR : _IOEOF;  
  51.                 stream->_cnt = 0;  
  52.                 return(_TEOF);  
  53.         }  
  54.   
  55.         if (  !(stream->_flag & (_IOWRT|_IORW)) &&  
  56.               ((_osfile_safe(_fileno(stream)) & (FTEXT|FEOFLAG)) ==  
  57.                 (FTEXT|FEOFLAG)) )  
  58.                 stream->_flag |= _IOCTRLZ;  
  59.         /* Check for small _bufsiz (_SMALL_BUFSIZ). If it is small and 
  60.            if it is our buffer, then this must be the first _filbuf after 
  61.            an fseek on a read-access-only stream. Restore _bufsiz to its 
  62.            larger value (_INTERNAL_BUFSIZ) so that the next _filbuf call, 
  63.            if one is made, will fill the whole buffer. */  
  64.         if ( (stream->_bufsiz == _SMALL_BUFSIZ) && (stream->_flag &  
  65.               _IOMYBUF) && !(stream->_flag & _IOSETVBUF) )  
  66.         {  
  67.                 stream->_bufsiz = _INTERNAL_BUFSIZ;  
  68.         }  
  69. #ifndef _UNICODE  
  70.         stream->_cnt--;  
  71.         return(0xff & *stream->_ptr++);  
  72. #else  /* _UNICODE */  
  73.         stream->_cnt -= sizeof(wchar_t);  
  74.         return (0xffff & *((wchar_t *)(stream->_ptr))++);  
  75. #endif  /* _UNICODE */  
  76.   
  77. }  

 

代码中分了三种情况:

1) 缓冲区不为空

此时, 把缓冲区中的数据复制到传入的字符数组中.

 

2) 缓冲区为空, 需要读取的数据大于缓冲的尺寸

此时, 直接调用函数_fread把文件中的内容写到传入的字符数组中.

 

3) 缓冲区为空, 需要读取的数据不大于缓冲的尺寸

此时, 调用函数_fread读满缓冲区, 并再写缓冲区的一个字符到传入的字符数组中.

 

若未读满传入的字符数组, 循环执行上述1~3过程, 直到读满或读到文件末尾(EOF).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值