C language file operation, introduction of fopen, fclose, fgetc, fgets, fputc, fputs, fwrite, fread, feof, ferror, fprintf, fscanf and other file functions

Article directory

  • Why use files
  • what is a file
  • file opening and closing
    • file pointer
    • file opening and closing
  • Sequential reading and writing of files
  • Compare a set of functions
  • Random reading and writing of files
    • fseek
    • ftell function
    • rewind function
  • text files and binary files
  • Judgment of the end of file reading
    • misused feof
  • file cache
    • practise

Why use files

When we learned about the structure and dynamic memory management functions, we wrote two versions of the address book. When the two versions of the address book are running, their data is stored in the memory. If it is not stored in our auxiliary storage, our data will be lost when the program ends. When we use the address book next time, the data has to be re-entered. This seems very troublesome. We thought about how to put the data in the memory into our auxiliary storage. That is using files. File management in C language is mainly realized through various functions. Next, we will introduce what a file is and the functions related to file operation in C language.

What is a file

Files on disk are files.
But in program design, we generally talk about two types of files: program files and data files (classified from the perspective of file functions).
Program files include .c files and obj files, which are all program files in C language.
Data files The content of a file is generally not a program, but data that is read and written at runtime, such as a file that needs to read data from when a program runs, or a file that outputs content. We’re now talking about data files.
Filename A file must have a unique file identifier for users to identify and reference.
The file name consists of 3 parts: file path + file name trunk + file suffix.
For example: c:\code\test.txt
For convenience, the file ID is often referred to as the file name.

File opening and closing

File pointer

In the cache file system, the key concept is “file type pointer”, referred to as “file pointer”.
Each used file has opened up a corresponding file information area in the memory, which is used to store the relevant information of the file (such as the name of the file)
word, file status and current location of the file, etc.). This information is stored in a structure variable. The struct type is defined by the system
Declared, named FILE.

file type structure

struct _iobuf {<!-- -->
        char *_ptr;
        int _cnt;
        char *_base;
        int_flag;
        int_file;
        int _charbuf;
        int _bufsize;
        char *_tmpfname;
       };
typedef struct _iobuf FILE

The content contained in the FILE type of different C compilers is not exactly the same, but the same.
Whenever a file is opened, the system creates a variable of the FILE structure and fills it with information. The user does not have to care about the details.
Generally, we maintain this variable through a pointer of FILE type.
The file associated with it can be found through the file pointer variable.

File opening and closing

The file should be opened and closed before reading and writing. When writing a program, when opening the file, a FILE* pointer variable will be returned to point to the file, which is equivalent to establishing the relationship between the pointer and the file.
ANSIC stipulates that fopen and fclose are used to open and close files. Some explanations of these two functions:
FILE * fopen ( const char * filename, const char * mode );
Introduction to the fopen function:
Open file
Opens the file whose name is specified in the argument filename and associates it with the stream that can be identified by the returned FILE pointer in future operations.
The operations allowed on the stream and how they are performed are defined by the mode parameter.
By default, the returned stream will be fully buffered if it is known not to refer to an interactive device.
The returned pointer can be disassociated from the file by calling fclose or freopen. All open files are automatically closed upon normal program termination.
The operating environment supports opening at least FOPEN_MAX files at the same time
Parameter explanation
file name
A C string containing the name of the file to open.
Its value should follow the runtime environment’s filename conventions and can include paths if supported by the system.
model
A C string containing the file access mode. it can be:
“r” read: Open file for input operation. The file must exist.
“w” write: Create an empty file for output operations. If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.
‘one’ Append: Open the file for output at the end of the file. Output operations always write data to the end of the file, extending it. Reposition operations (fseek, fsetpos, rewind) are ignored. If the file does not exist, it is created.
“r + ” read/update: Open file for update (input and output). The file must exist.
“w + ” write/update: create an empty file and open it for update (for input and output). If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.
“A+” Append/Update: Opens a file for update (input and output), all output operations write data at the end of the file. Repositioning operations (fseek, fsetpos, rewind) affect the next input operation, but output operations move the position back to the end of the file. If the file does not exist, it is created.
With the above mode specifiers, the file will be opened as a text file. In order to open a file as binary, the “b” character must be included in the mode string. This extra “b” character can be appended to the end of the string (thus forming the following compound patterns: “rb”, “wb”, “ab”, “r+b”, “w+b”, “a+b” ), or between letters and mixed-mode “+” symbols (“rb + “, “wb + “, “ab + “).
The new C standard (C2011, not part of C++) adds a new standard subspecifier (“x”) that can be appended to any “w” specifier (forming “wx”, “wbx”, “w+ x” or “w+bx”/”wb+x”). This sub-specifier forces the function to fail if the file exists, rather than overwriting it.
If the sequence is followed by other characters, the behavior depends on the library implementation: some implementations may ignore other characters in order to e.g. accept an extra “t” (sometimes used to explicitly declare text files).
In some library implementations, opening or creating a text file with update mode may treat the stream as a binary file.
Return value explanation
If the file is successfully opened, the function returns a pointer to a FILE object that can be used to identify the stream in future operations. Otherwise, a null pointer will be returned. In most library implementations, the errno variable is also set to a system-specific error code on failure.
fclose function explanation
close file
Closes the file associated with the stream and disassociates it.
All internal buffers associated with the stream are disassociated from the stream and flushed: the contents of any unwritten output buffers are written, and the contents of any unread input buffers are discarded.
Even if the call fails, the stream passed as an argument will no longer be associated with the file and its buffers.
Function prototype
int fclose ( FILE * stream );
Function parameter explanation
Pointer to a FILE object specifying the stream to close.
Explanation of function return values
Returns a value of zero if the stream was successfully closed.
On failure, EOF is returned.

Example of use:

//The use of fopen function and fclose function
int main()
{<!-- -->
\t//open a file
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return 1;
}
//Using the file //Pointer to the file type
//fputc('a', pf);
// close the file
fclose(pf);
pf = NULL;
return 0;
}

Sequential reading and writing of files

Introduction to Flow
Standard output stream: stdout
Standard input stream: stdin
Standard error stream: std

The functions involved in the sequential reading and writing of files

Introduction to each function
fgetc function

fputc function

fgets function

fputs function

fscanf function

fprintf function

fread function

fwrite function

Use of the above functions

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

//Use of fopen function and fclose function
int main()
{<!-- -->
\t//open a file
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return 1;
}
//Using the file //Pointer to the file type
//fputc('a', pf);
// close the file
fclose(pf);
pf = NULL;
return 0;
}

//Use of fputc
output from memory to file
int main()
{<!-- -->
\t//open a file
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return 1;
}
//Using the file //Pointer to the file type
for (int i = 0; i < 26; i ++ )
{<!-- -->
fputc('a' + i, pf);
}
// close the file
fclose(pf);
pf = NULL;
return 0;
}

//fgetc reads from file into memory
int main()
{<!-- -->
\t//open a file
FILE* pf = fopen("test.txt", "r"); //Every time the file is opened by writing, the content in the file will be updated
if (pf == NULL)
{<!-- -->
//Using the file //Pointer to the file type
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c", ch);
// close the file
fclose(pf);
pf = NULL;
return 0;
}

//Use of fputs
int main()
{<!-- -->
\t//open a file
FILE* pf = fopen("test.txt", "w");
fputs("hello world\\
", pf);
fputs("hello c\\
", pf);
// close the file
fclose(pf);
pf = NULL;
return 0;
}

//Use of fgets
int main()
{<!-- -->
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{<!-- -->
perror("fopen");
return 1;
}
char arr[10] = {<!-- --> 0 };
fgets(arr,6,pf); //When reading in, it will write '\0' in the last digit
printf("%s\\
", arr);
fclose(pf);
pf = NULL;
return 0;
}

//Use of fprintf function
struct S
{<!-- -->
int a;
float b;
char arr[10];
};
int main()
{<!-- -->
struct S s = {<!-- --> 100,3.0f,"lisi"};
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return 1;
}
fprintf(pf, "%d %f %s", s.a, s.b, s.arr);
fclose(pf);
pf = NULL;
return 0;
}

int main()
{<!-- -->
struct S m = {<!-- --> 0 };
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{<!-- -->`

> insert code snippet here

`
perror("fopen");
return 1;
}
int a = fscanf(pf, "%d %f %s", &m.a, &m.b, m.arr);
printf("%d %f %s", m.a, m.b, m.arr);
fclose(pf);
pf = NULL;
}


Compare a set of functions

scanf / fscanf / sscanf
printf / fprintf / sprintf
scanf formatted input function for the standard input stream
printf formatted output function for the standard output stream
fscanf formatted input function for all input streams
fprintf formatted output function for all output streams
sprintf writes a formatted data to a string
sscanf converts a string into corresponding formatted data

int main()
{<!-- -->
//When serializing and deserializing

struct S s = {<!-- --> 200, 3.5f, "wangwu" };
// Convert a structure to a string
char arr[200] = {<!-- --> 0 };
sprintf(arr, "%d %f %s\\
", s.n, s.f, s.arr);
printf("String data: %s\\
", arr);

//Convert a string to the corresponding formatted data
struct S tmp = {<!-- --> 0 };
sscanf(arr, "%d %f %s", &(tmp.n), &(tmp.f), tmp.arr);
printf("Formatted data: %d %f %s\\
", tmp.n, tmp.f, tmp.arr);

return 0;
}

Random reading and writing of files

fseek

Position the file pointer based on the position and offset of the file pointer
Function prototype:
int fseek ( FILE * stream, long int offset, int origin );
Function introduction:

Usage example

int main()
{<!-- -->
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return;
}
fseek(pf, 9, SEEK_CUR);
fputs("abc", pf);
fclose(pf);
pf = NULL;
return 0;
}

Run results

ftell function

Function prototype

long int ftell ( FILE * stream );

Function explanation

Usage example

int main()
{<!-- -->
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return;
}
fseek(pf, 9, SEEK_CUR); //9 + 3
fputs("abc", pf);
int a = ftell(pf);
printf("%d\\
", a); //9 + 3
fclose(pf);
pf = NULL;
return 0;
}

Run results

rewind function

Function explanation

Return the position of the file pointer to the beginning of the file

Usage example

int main()
{<!-- -->
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return;
}
fseek(pf, 9, SEEK_CUR); //9 + 3
fputs("abc", pf);
rewind(pf);
int a = ftell(pf);
printf("%d\\
", a); //9 + 3
fclose(pf);
pf = NULL;
return 0;
}

Run results

Text files and binary files

Depending on how the data is organized, data files are called text files or binary files.
The data is stored in binary form in memory, if it is output to external storage without conversion, it is a binary file.
If it is required to store in the form of ASCII code on the external memory, it needs to be converted before storing. A file stored in ASCII characters is a text file.
How is a piece of data stored in memory?
Characters are all stored in ASCII form, and numeric data can be stored in either ASCII or binary form.
If there is an integer 10000, if the ASCII code is output to the disk, it will occupy 5 bytes on the disk (one byte for each character), while the binary output will only occupy 4 bytes on the disk

Test Code

int main()
{<!-- -->
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{<!-- -->
perror("fopen");
return;
}
fwrite( &a, sizeof(a), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}


Determination of the end of file reading

Wrongly used feof

Keep in mind: during the file reading process, the return value of the feof function cannot be used to directly determine whether the file is over. But Applied to when the file reading ends, it is judged whether the reading fails to end, or the end of the file is encountered
1. Whether the reading of the text file is over, judge whether the return value is EOF (fgetc), or NULL (fgets)
fgetc encounters the end of the file, returns EOF, and sets a state at the same time, encounters the end of the file. Use feof to detect this state,
When an error is encountered, EOF is returned, and a state is set at the same time. When an error is encountered, ferror is used to detect this state.
fgets determines whether the return value is NULL.
2. Judging the end of reading the binary file, and judging whether the return value is less than the actual number to be read.
For example: fread judges whether the return value is less than the actual number to be read.

Correct usage:
Example of a text file:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{<!-- -->
int c = 10; // Note: int, not char, requires handling EOF
FILE* fp = fopen("test.txt", "r");
if (!fp) {<!-- -->
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc will return EOF when reading fails or encounters the end of the file
while ((c = fgetc(fp)) != EOF) // standard C I/O read file loop
{<!-- -->
putchar(c);
}
//Determine what is the reason for the end
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}

Run result:

Binary files

#include <stdio.h>
enum {<!-- --> SIZE = 5 };
int main(void)
{<!-- -->
double a[SIZE] = {<!-- --> 1.,2.,3.,4.,5.};
FILE* fp = fopen("test.bin", "wb"); // must use binary mode
fwrite(a, sizeof(*a), SIZE, fp); // write array of double
fclose(fp);
double b[SIZE];
fp = fopen("test.bin", "rb");
size_t ret_code = fread(b, sizeof * b, SIZE, fp); // read double array
if (ret_code == SIZE) {<!-- -->
puts("Array read successfully, contents: ");
for (int n = 0; n < SIZE; + + n) printf("%f ", b[n]);
putchar('\\
');
}
else {<!-- --> // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\\
");
else if (ferror(fp)) {<!-- -->
perror("Error reading test.bin");
}
}
fclose(fp);
}

Run result:

File cache

The ANSIC standard employs data files processed by the “Cache File System”. The so-called buffer file system means that the system automatically creates a ““file buffer” in the memory for each file being used in the program. The output data from the memory to the disk will be sent to the memory first The buffer in the buffer is sent to the disk together after the buffer is filled. If data is read from the disk to the computer, the data is read from the disk file and input to the memory buffer (full buffer), and then from the buffer Send data to the program data area one by one (program variables, etc.). The size of the buffer is determined by the C compilation system.


Let’s look at an example:

#include <stdio.h>
#include <Windows.h>
int main()
{<!-- -->
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{<!-- -->
perror("fopen");
return;
}
printf("Sleep for 10 seconds - the data has been written, open the test.txt file and find that the file has no content\\
");
Sleep(10000);
printf("Refresh buffer\\
");
fflush(pf);//When the buffer is flushed, the data in the output buffer is written to the file (disk)
//Note: fflush cannot be used on higher versions of VS
printf("Sleep for another 10 seconds - at this time, open the test.txt file again, the file has content\\
");
Sleep(10000);
fclose(pf);
//Note: fclose will also refresh the buffer when closing the file
pf = NULL;
return 0;
}

A conclusion can be drawn here:
Because of the existence of the buffer, when the C language operates the file, it needs to refresh the buffer or close the file at the end of the file operation.
Failure to do so can cause problems reading and writing files.

Exercise

Copy the contents of one file to another
Code:

#include <stdio.h>
int main()
{<!-- -->
\t//open a file
FILE* pRead = fopen("test1.txt", "r");
if (pRead == NULL)
{<!-- -->
perror("fopen");
return;
}
FILE* pWrite = fopen("test2.txt", "w");
if (pWrite == NULL)
{<!-- -->
fclose(pRead);
pRead = NULL;
perror("fopen");
return;
}
//Copy the contents of test1 to test2
char ch = '0';
while ((ch = fgetc(pRead)) != EOF)
{<!-- -->
fputc(ch, pWrite);
}
fclose(pRead);
pRead = NULL;
fclose(pWrite);
pWrite = NULL;
return 0;
}

Results of the:

Preprocessing for the next program.