走进C标准库(5)——"stdio.h"中的其他部分函数

函数介绍来自:http://ganquan.info/standard-c/

函数名: freopen 
功  能: 替换一个流 
用  法: FILE *freopen(char *filename, char *type, FILE *stream); 

 1 FILE * __cdecl _tfreopen (
 2         const _TSCHAR *filename,
 3         const _TSCHAR *mode,
 4         FILE *str
 5         )
 6 {
 7         REG1 FILE *stream;
 8         FILE *retval;
 9 
10         _ASSERTE(filename != NULL);
11         _ASSERTE(*filename != _T('\0'));
12         _ASSERTE(mode != NULL);
13         _ASSERTE(str != NULL);
14 
15         /* Init stream pointer */
16         stream = str;
17 
18         _lock_str(stream);
19 
20         /* If the stream is in use, try to close it. Ignore possible
21          * error (ANSI 4.9.5.4). */
22         if ( inuse(stream) )
23                 _fclose_lk(stream);
24 
25         stream->_ptr = stream->_base = NULL;
26         stream->_cnt = stream->_flag = 0;
27         retval = _openfile(filename,mode,_SH_DENYNO,stream);
28 
29         _unlock_str(stream);
30         return(retval);
31 }

调用_fclose_lk断开流stream与文件的连接,然后将stream中的变量恢复到初始状态,并释放其中分配的缓冲区。最后调用_openfile重新建立新的文件描述符以及文件句柄之间的连接。

函数名: fclose()
功 能: 关闭一个流。
用 法: int fclose(FILE *stream);
 1 int __cdecl fclose (
 2         FILE *str
 3         )
 4 {
 5         REG1 FILE *stream;
 6         REG2 int result = EOF;
 7 
 8         /* Init near stream pointer */
 9         stream = str;
10 
11         if (stream->_flag & _IOSTRG) {
12                 stream->_flag = 0;
13                 return(EOF);
14         }
15 
16 #endif  /* _MT */
17 
18         _ASSERTE(str != NULL);
19 
20         if (inuse(stream)) {
21 
22                 /* Stream is in use:
23                        (1) flush stream
24                        (2) free the buffer
25                        (3) close the file
26                        (4) delete the file if temporary
27                 */
28 
29                 result = _flush(stream);
30                 _freebuf(stream);
31 
32                 if (_close(_fileno(stream)) < 0)
33                         result = EOF;
34 
35                 else if ( stream->_tmpfname != NULL ) {
36                         /*
37                          * temporary file (i.e., one created by tmpfile()
38                          * call). delete, if necessary (don't have to on
39                          * Windows NT because it was done by the system when
40                          * the handle was closed). also, free up the heap
41                          * block holding the pathname.
42                          */
43                         _free_crt(stream->_tmpfname);
44                 stream->_tmpfname = NULL;
45                 }
46 
47         }
48 
49         stream->_flag = 0;
50         return(result);
51 }

调用_freebuf,将stream中的变量恢复到初始状态,都是释放其中分配的缓冲区。然后调用_close(_fileno(stream)),在文件描述符管理单元删除该文件描述符,并调用CloseHandle关闭在操作系统中打开的文件句柄。如果有临时文件名被使用,也释放掉。

函数名: feof 
功  能: 检测流上的文件结束符 
用  法: int feof(FILE *stream); 

1 int __cdecl feof (
2         FILE *stream
3         )
4 {
5         return( ((stream)->_flag & _IOEOF) );
6 }

返回检测stream->_flag是否被标记了_IOEOF,该标记在其它函数中遇到EOF情形时被添加。

函数名: ferror 
功  能: 检测流上的错误 
用  法: int ferror(FILE *stream); 

1 int __cdecl ferror (
2         FILE *stream
3         )
4 {
5         return( ((stream)->_flag & _IOERR) );
6 }

返回检测stream->_flag是否被标记了_IOERR。

函数名: fflush 
功 能: 清除文件缓冲区,文件以写方式打开时将缓冲区内容写入文件
用  法: int fflush(FILE *stream); 

 1 int __cdecl fflush (
 2         REG1 FILE *str
 3         )
 4 {
 5 
 6         /* if stream is NULL, flush all streams */
 7         if ( str == NULL ) {
 8                 return(flsall(FFLUSHNULL));
 9         }
10 
11         if (_flush(str) != 0) {
12                 /* _flush failed, don't attempt to commit */
13                 return(EOF);
14         }
15 
16         /* lowio commit to ensure data is written to disk */
17         if (str->_flag & _IOCOMMIT) {
18                 return (_commit(_fileno(str)) ? EOF : 0);
19         }
20         return (0);
21 }

当传入fflush中的stream为NULL时,则_fflush所有写的流。否则,_fflush当前stream,成功的话,调用_commit写入到硬盘中。

flsall中FFLUSHNULL参数意义如下:

/*
* FFLUSHNULL functionality: fflush the write
* stream and kept track of the error, if one
* occurs
*/

在_fflush()函数中会使用:

stream->_ptr = stream->_base;
stream->_cnt = 0;

将缓冲区清空。

如果stream仅具有写标记且buffer已成功分配(if ((stream->_flag & (_IOREAD | _IOWRT)) == _IOWRT && bigbuf(stream) && (nchar = stream->_ptr - stream >_base) > 0))的话,则调用_write将缓冲区里的内容写到相关的文件句柄中。

注意,操作系统为了减少读写硬盘的次数,也会建立文件缓冲区,并不立刻将数据写入文件,而是先把数据累计到缓冲区,再以块为单位批量输出到文件中。

_write中调用的WriteFile就是将相关内容写到了文件句柄对应的缓冲区中。这一点可以从putc函数产生的现象看到。当执行一句putc语句时,内容并没有马上被写入到流对应的文件中的。

而fflush会强制将I/O控制块缓冲区的内容写入到硬盘中。这就通过在_commit中调用FlushFileBuffers,强行将文件句柄对应的缓冲区的内容写入到了硬盘中。

函数名: fgetpos 
功  能:取得当前文件的指针所指的位置,并把该指针所指的位置数存放到pos所指的对象中。pos值以内部格式存储,仅由fgetposfsetpos使用。其中fsetpos的功能与fgetpos相反,为了详细介绍,将在后节给与说明。

返回值:成功返回0,失败返回非0,并设置errno

用  法: int fgetpos(FILE *stream);

 1 int __cdecl fgetpos (
 2         FILE *stream,
 3         fpos_t *pos
 4         )
 5 {
 6 #ifdef _MAC
 7         int posl = ftell(stream);
 8 
 9         *pos = (fpos_t) posl;
10 
11         if ( posl != -1L )
12                 return(0);
13         else
14                 return(-1);
15 }

通过ftell获得当前文件指针相对文件首的偏移字节,将该位置存储到一个fpos_t对象中。如果ftell是成功的,则返回0表示fgetpos成功,否则,返回-1。

函数名: fsetpos 
功  能:将文件指针定位在pos指定的位置上。该函数的功能与前面提到的fgetpos相反,是将文件指针fp按照pos指定的位置在文件中定位。pos值以内部格式存储,仅由fgetposfsetpos使用。

返回值:成功返回0,否则返回非0

用  法: int fsetpos(FILE *stream, const fpos_t *pos); 

int __cdecl fsetpos (
        FILE *stream,
        const fpos_t *pos
        )
{
        return( fseek(stream, (long) *pos, SEEK_SET) );
}

直接调用fseek,在参数为SEEK_SET的情况下实现。

函数名: fread 
功  能: 从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功返回 0 
用  法: int fread(void *ptr, int size, int nitems, FILE *stream); 

 1 size_t __cdecl fread (
 2         void *buffer,
 3         size_t size,
 4         size_t num,
 5         FILE *stream
 6         )
 7 {
 8         char *data;                     /* point to where should be read next */
 9         unsigned total;                 /* total bytes to read */
10         unsigned count;                 /* num bytes left to read */
11         unsigned bufsize;               /* size of stream buffer */
12         unsigned nbytes;                /* how much to read now */
13         unsigned nread;                 /* how much we did read */
14         int c;                          /* a temp char */
15 
16         /* initialize local vars */
17         data = buffer;
18 
19         if ( (count = total = size * num) == 0 )
20                 return 0;
21 
22         if (anybuf(stream))
23                 /* already has buffer, use its size */
24                 bufsize = stream->_bufsiz;
25         else
26                 bufsize = BUFSIZ;
27         /* here is the main loop -- we go through here until we're done */
28         while (count != 0) {
29                 /* if the buffer exists and has characters, copy them to user
30                    buffer */
31                 if (anybuf(stream) && stream->_cnt != 0) {
32                         /* how much do we want? */
33                         nbytes = (count < (unsigned)stream->_cnt) ? count : stream->_cnt;
34                         memcpy(data, stream->_ptr, nbytes);
35 
36                         /* update stream and amt of data read */
37                         count -= nbytes;
38                         stream->_cnt -= nbytes;
39                         stream->_ptr += nbytes;
40                         data += nbytes;
41                 }
42                 else if (count >= bufsize) {
43                         /* If we have more than bufsize chars to read, get data
44                            by calling read with an integral number of bufsiz
45                            blocks.  Note that if the stream is text mode, read
46                            will return less chars than we ordered. */
47 
48                         /* calc chars to read -- (count/bufsize) * bufsize */
49                         nbytes = ( bufsize ? (count - count % bufsize) :
50                                    count );
51 
52                         nread = _read(_fileno(stream), data, nbytes);
53                         if (nread == 0) {
54                                 /* end of file -- out of here */
55                                 stream->_flag |= _IOEOF;
56                                 return (total - count) / size;
57                         }
58                         else if (nread == (unsigned)-1) {
59                                 /* error -- out of here */
60                                 stream->_flag |= _IOERR;
61                                 return (total - count) / size;
62                         }
63 
64                         /* update count and data to reflect read */
65                         count -= nread;
66                         data += nread;
67                 }
68                 else {
69                         /* less than bufsize chars to read, so call _filbuf to
70                            fill buffer */
71                         if ((c = _filbuf(stream)) == EOF) {
72                                 /* error or eof, stream flags set by _filbuf */
73                                 return (total - count) / size;
74                         }
75 
76                         /* _filbuf returned a char -- store it */
77                         *data++ = (char) c;
78                         --count;
79 
80                         /* update buffer size */
81                         bufsize = stream->_bufsiz;
82                 }
83         }
84 
85         /* we finished successfully, so just return num */
86         return num;
87 }

在fread中处理3种情况:I/O控制块缓冲区中有剩余数据,则直接从中拿出需要的数据(如果剩余数据不够,则只拿缓冲区里有的);无剩余数据且仍需要的数据数目大于等于缓冲区中已有的数据数目,通过调用_read直接从stream对应的文件中读取需要的数据(需要注意的是,在这种情况下,get data by calling read with an integral number of bufsiz blocks);无剩余数据且需要的数据数目小于缓冲区,则调用_filbuf新建缓冲区,同时接受_filbuf返回的一个char保证数据正确。

三种情况在一个循环中,直到需要的数据全取出即可。

简单来说就是,缓冲区有数据,就取出来;没了,就到内存拿整数个缓冲区大小的数据,模拟走缓冲区取数据的过程;最后剩余部分,填入缓冲区后取出。

函数名: fseek 
功  能: 重定位流上的文件指针 
用  法: int fseek(FILE *stream, long offset, int fromwhere); 

使用setFilePointer移动了文件指针,不是移动缓冲区的当前指针。

函数名: ftell 
功  能: 返回当前文件指针 
用  法: long ftell(FILE *stream); 

调用_lseek,找到当前文件首的地址,再加上当前缓冲区内的指针偏移量,得到在文件中的文件指针。(不必纠结于文件指针本身的概念,其意义就是当前指向的文件数据的地址)

函数名: fwrite 
功  能: 写内容到流中 
用  法: int fwrite(void *ptr, int size, int nitems, FILE *stream); 

fread的相反过程,无需细述。

原型:extern void printf(const char *format,...);  

用法:#include <stdio.h>
功能:格式化字符串输出

主要实现了各种控制符的逻辑,WriteConsole实现了到控制台的输出。

其他的函数就暂时不讨论了。

就算黑夜也有星光。
原文地址:https://www.cnblogs.com/shijiezhenmei/p/3681840.html