Streaming and buffering

    For unbuffered I/O libraries, most operations revolve around file descriptors. When a file is opened, a file descriptor is returned, which is then used for subsequent I/O operations. As for the standard I/O library, its operation is carried out around the stream (stream). When a file is opened or created with the standard I/O library, a stream is associated with the file.
    Standard I/O file streams can be used with either single-byte or multi-byte ("wide") character sets, depending on the stream's orientation. The stream is initially created with no orientation. If a multibyte I/O function (see <wchar.h>) is used on the stream at this time, the orientation of the stream is set to wide; otherwise, it is set to byte-oriented. There are only two functions that change the orientation of the stream. The fwide function can be used to set the orientation of a stream, and the freopen function (see Open Stream Function) can be used to clear the orientation of a stream.
#include<stdio.h>
#include<wchar.h>
int fwide(FILE *fp, int mode);
    /* Return value: positive for wide-oriented streams; negative for byte-oriented streams; 0 for undirected streams */

    Depending on the value of the mode parameter, the fwide function performs different work.
    1. If mode is negative, fwide will attempt to make the specified stream byte-oriented.
    2. If mode is positive, fwide will try to make the specified stream wide oriented.
    3. If mode is 0, fwide will not attempt to set the stream orientation, but returns a value that identifies the stream orientation.
    Note that fwide does not change the orientation of an already directed stream, and no error is returned. The only thing we can rely on is to clear errno before calling fwide and check the value of errno after fwide returns.
    When opening a stream, the standard I/O function fopen returns a file pointer to a FILE object. This object is usually a structure that contains all the information the standard I/O library needs to manage the stream, including the file descriptor used for the actual I/O, a pointer to the buffer used for the stream, and the length of the buffer , the number of characters currently in the buffer, and the error flag. The application does not have to examine the FILE object. To reference a stream, pass a FILE pointer as an argument to every standard I/O function.
    Three streams are predefined for a process, namely standard input, standard output and standard error, which are referenced by three predefined file pointers stdin, stdout and stderr defined in the header file <stdio.h> respectively. These streams are automatically used by processes, and they refer to the same files as the file descriptors STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO, respectively.
    The system default buffer type for any given stream can also be changed using one of the following two functions (which should be called before performing any other operation on opening the stream).
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
                        /* Return value: if successful, return 0; otherwise, return non-0 */

    The buffering mechanism can be turned on or off using the setbuf function. For buffered I/O, parameter buf must point to a buffer of length BUFSIZE (this constant is located in <stdio.h>). Usually the stream is fully buffered after this, but some systems may also set it to be line-buffered if the stream is associated with an end device. To turn off buffering, set buf to NULL.
    Using setvbuf, we can use the mode parameter to precisely specify the required buffer type:
    1, _IOFBF: full buffer;
    2, _IOLBF: line buffer;
    3, _IONBF: no buffer.
    If an unbuffered stream is specified, the buf and size parameters are ignored. If full buffering or line buffering is specified, buf and size optionally specify a buffer and its length. If the stream is buffered and buf is NULL, the standard I/O library will automatically allocate a buffer of the appropriate length for the stream according to the value of BUFSIZE (some C library implementations use the member st_blksize in the stat structure to value to determine the optimal I/O buffer length).
    The following table summarizes the actions of these two functions, along with their various options.

    If a standard I/O buffer of an automatic variable class is allocated within a function, the stream must be closed before returning from the function. In addition, some implementations use a portion of the buffer for its own management operation information, so the actual number of bytes of data that can be stored in the buffer is less than size. In general, the length of the buffer should be chosen by the system, and the buffer should be allocated automatically. When the stream is closed in this case, the standard I/O library will automatically free the buffer.
    At any time, a stream can be forced to be flushed using the fflush function, which transfers all unwritten data in the stream to the kernel. If its argument fp is NULL, this function will cause all output streams to be flushed.
#include <stdio.h>
int fflush(FILE *fp); /* Return value: if successful, return 0; otherwise, return EOF */

    To open a standard I/O stream, use the following three functions.
#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);
              /* Return value: if successful, return the file pointer; otherwise, return NULL */

    The differences between these three functions are as follows:
    1. The fopen function opens a specified file whose path name is pathname.
    2. freopen opens a specified file on a specified stream. If the stream is already open, close the stream first. If the stream is already directed, use freopen to clear the direction. This function is generally used to open a specified file as a predefined stream: standard input, standard output, or standard error.
    3. fdopen takes an existing file descriptor and combines a standard I/O stream with the descriptor. This function is commonly used with descriptors returned by create pipe and network communication pipe functions. Because these special types of files cannot be opened with the standard I/O function fopen, you must first call the device-specific function to obtain a file descriptor, and then use fdopen to combine a standard I/O stream with it.
    The type parameter specifies how to read and write to the stream. Its optional values ​​are shown in the following table.

    Using the character b as part of type here enables the standard I/O system to distinguish text files from binary files. Because the UNIX kernel does not distinguish between these two kinds of files, specifying the character b has no practical effect in a UNIX environment.
    For fdopen, the meaning of the type parameter is slightly different. Because the descriptor is already open, fdopen opens for writing and does not truncate the file.
    When a file is opened with the append write type, each write writes data to the current end of the file. If multiple processes open the same file with standard I/O append writing, the data from each process will be properly written to the file.
    When opening a file as read and write, the following restrictions apply:
    * Input cannot be directly followed by output without fflush, fseek, fsetpos, or rewind in between.
    * If there is no fseek, fsetpos or rewind in between, or an input operation does not reach the end of the file, output cannot be directly followed by an input operation.
    To close a stream, use the fclose function.
#include <stdio.h>
int fclose(FILE *fp); /* Return value: if successful, return 0; otherwise, return EOF */

    Before the file is closed, flush the output data in the buffer, discarding any input data in the buffer. If the standard I/O library has automatically allocated a buffer for the stream, the buffer is freed.
    When a process terminates normally, all standard I/O streams with unwritten buffered data are flushed and all open standard I/O streams are closed.

    In addition, memory streams are also supported in SUSv4. Although the FILE pointer is still used for access, there is no underlying file. All I/O is done by transferring bytes back and forth between the buffer and main memory. Because buffer overflows are avoided, memory streams are ideal for creating strings. Because memory streams only access main memory, not disk files, there is a big performance improvement for functions that take standard I/O streams as arguments to temporary files.
    There are 3 functions available for memory stream creation.
#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
FILE *open_memstream(char **bufp, size_t *sizep);
#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
                          /* Return value: if successful, return stream pointer; otherwise, return NULL */

    The fmemopen function allows the caller to provide a buffer for memory streaming: the buf parameter points to the start of the buffer, and size specifies the size of the buffer in bytes. If buf is empty, fmemopen will allocate a buffer of size bytes. In this case, the buffer is freed when the stream is closed. The type parameter controls how the stream is used, and its possible values ​​are shown in the following table.

    Note that these values ​​are slightly different from the value of the type parameter for standard I/O streams.
    1. Whenever the memory stream is opened by append writing, the current file position is set to the first null byte in the buffer, so the memory stream is not suitable for storing binary data (the binary data may be before the end of the data. contains multiple null bytes). If there are no null bytes in the buffer, it is set to the byte after the end of the buffer. When the stream is not opened for append writing, the current position is set to the beginning of the buffer.
    2. If the buf parameter is a null pointer, it is meaningless to open the stream for reading or writing. Because the buffer is allocated via fmemopen at this point, there is no way to find the address of the buffer.
    3. Anytime you need to increase the amount of data in the stream buffer and call fflush, fseek, fseeko and fsetpos, a null byte will be written in the current position.
    The stream created by the open_memstream function is byte-oriented, and the stream created by the open_wmemstream function is wide-byte-oriented. The difference between them and the fmemopen function is:
    1. The created stream can only be opened for writing.
    2. You cannot specify your own buffer, but you can access the buffer address and size through the bufp and sizep parameters respectively.
    3. After closing the stream, you need to release the buffer by yourself.
    4. Adding bytes to the stream increases the buffer size.
    However, some principles must be followed in the use of buffer addresses and sizes. First, the buffer address and length are only valid after a call to fclose or fflush; second, these values ​​are not valid until the next stream write or call to fclose. Because the buffer can grow, it may need to be reallocated, at which point the buffer's memory address value changes on the next call to fclose or fflush.
    The following program uses the fmemopen function to demonstrate how stream writes operate when a buffer is filled with a known pattern.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BSZ	48

int main(){
	FILE *fp;
	char buf[BSZ];
	
	memset(buf, 'a', BSZ-2);
	buf[BSZ-2] = '\0';
	buf[BSZ-1] = 'X';
	printf("after memset: %s, len = %ld\n", buf, (long)strlen(buf));
	if((fp=fmemopen(buf, BSZ, "w+")) == NULL){
		printf("fmemopen failed\n");
		exit(1);
	}
	printf("initial buffer contents: %s\n", buf);
	fprintf(fp, "hello, world");
	printf("before fflush: %s\n", buf);
	fflush(fp); // the file pointer is now at strlen("hello, world") bytes
	printf("after fflush: %s\n", buf);
	printf("len of string in buf = %ld\n", (long)strlen(buf));

	memset(buf, 'b', BSZ-2);
	buf[BSZ-2] = '\0';
	buf[BSZ-1] = 'X';
	printf("after memset: %s, len = %ld\n", buf, (long)strlen(buf));
	fprintf(fp, "hello, world");
	fseek(fp, 0, SEEK_SET); // reset the file pointer to the beginning of buf
	printf("after fseek: %s, len = %ld\n", buf, (long)strlen(buf));

	memset(buf, 'c', BSZ-2);
	buf[BSZ-2] = '\0';
	buf[BSZ-1] = 'X';
	printf("after memset: %s, len = %ld\n", buf, (long)strlen(buf));
	fprintf(fp, "hello, world");
	fclose(fp);
	printf("after fclose: %s, len = %ld\n", buf, (long)strlen(buf));

	exit(0);
}

    operation result:
$ ./fmemopenDemo.out
after memset: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, len = 46
initial buffer contents: # fmemopen puts null bytes at the beginning of the buffer
before fflush: # The buffer will not change until the stream is flushed
after fflush: hello, world
len of string in buf = 12 # append null bytes to end of string
after memset: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, len = 46
after fseek: bbbbbbbbbbbbhello, world, len = 24 # fseek causes buffer flushing and appends null bytes
after memset: cccccccccccccccccccccccccccccccccccccccccccccc, len = 46
after fclose: hello, worldcccccccccccccccccccccccccccccccccc, len = 46 # No additional null bytes are written
$

    This example shows the strategy of flushing the memory stream and appending null bytes. Null bytes are automatically appended when writing to the memory stream and advancing the stream's content size (which is fixed relative to the buffer). The size of the stream content is determined by how much is written.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326181543&siteId=291194637