preface
In C language, file is an important concept in programming. The operating system manages data in the unit of files. Although there may not be many places to use files in the future, files are still an indispensable part of C language. Well, no more nonsense. Let's get straight to the point.
1, What is a file
Concept: files on disk are files.
But in programming, we generally read two kinds of files: program file and data file.
Procedure documents:
It includes source program files (suffix. c), object files (suffix. obj in windows Environment), and executable programs (suffix. exe in windows Environment).
Data file:
The content of the file is not necessarily the program, but the data read and written when the program runs, such as the file from which the program needs to read data or the file that outputs the content.
Today we are talking about data files.
Before that, the input and output of data we processed were targeted at the terminal, that is, input data from the keyboard of the terminal and display the operation results on the display.
In fact, sometimes we will output information to the disk, and then read the data from the disk to the memory when necessary. What we deal with here is the files on the disk.
2, File name
A file should have a unique file ID to enable users to identify and reference.
The file name consists of three 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.
3, File type
According to the organization form of data, data files are called text files or binary files.
Data is stored in binary form in memory. If it is output to external memory without conversion, it is a binary file.
If it is required to store in the form of ASC Ⅱ code on external memory, it needs to be converted before storage. The file stored in the form of ASC Ⅱ characters is a text file.
So how is a data stored in memory?
All characters are stored in ASC Ⅱ form, and numerical data can be stored in ASC Ⅱ form or binary form.
If there is an integer of 10000:
1. Text description
If it is output to the disk in the form of ASC Ⅱ code, the disk occupies 5 bytes (each character occupies one byte),
The binary output only takes up 4 bytes on the disk (10000 is an integer without any conversion, so it takes up 4 bytes).
2. Image interpretation
3. Code interpretation
Example 1:
#include<stdio.h> int main() { int a = 10000; FILE* pf = fopen("test.txt", "wb");// fopen: create //"WB" -> write binary is written in binary form fwrite(&a, 4, 1, pf);// Write to file in binary form fclose(pf);// Close file pf = NULL; return 0; }
Through such a code, we can see that such a file appears under the folder:
If we open it directly, there is the so-called garbled code inside, and we can't understand it, how can we understand it?
The compiler used by bloggers here is: Visual Studio 2019
① First add the test.txt file to the project:
② Select open mode - > binary mode:
③ Open the file test.txt:
What we see is: 10 27 00 00. If we want to read in memory, we should read in reverse order, because this is small end storage, and get: 00 00 27 10
Then we find that the hexadecimal number is converted to decimal, which is exactly 10000
4, File buffer
ANSIC standard adopts "buffer file system" to process data files. The so-called buffer file system refers to that the system automatically opens up a "file buffer" for each file in use in the program in memory.
Data output from memory to disk will be sent to the buffer in memory first, and then sent to disk together after the buffer is filled.
If you read data from the disk to the computer, read the data from the disk file, input it into the memory buffer (fill the buffer), and then send the data from the buffer to the program data area (program variables, etc.) one by one.
The size of the buffer is determined according to the C compilation system.
The diagram is as follows:
5, File pointer
In the buffered file system, the key concept is "file type pointer", which is referred to as "file pointer".
Each used FILE opens up a corresponding FILE information area in the memory to store the relevant information of the FILE (such as the name of the FILE, the status of the FILE, the current location of the FILE, etc.). This information is stored in a structure variable. The structure type is declared by the system and named FILE.
You can view the definition of the FILE structure in the header FILE of studio. H (located in the include folder under the visual studio installation directory), as follows:
struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE;
The FILE types of different compilers contain different contents, but they are similar.
Whenever a FILE is opened, the system will automatically create a variable of FILE structure according to the situation of the FILE and fill in the information in it. Users don't have to care about the details.
Generally, the variables of the FILE structure are maintained through a FILE pointer, which is more convenient to use.
Next, we can create a pointer variable of FILE *:
FILE* pf;// File pointer variable
PF is defined as a pointer variable pointing to FILE type data, which can make pf point to a FILE information area (a structure variable). The FILE can be accessed through the information in the FILE information area. That is, the FILE associated with it can be found through the FILE pointer variable.
As shown in the figure:
6, Opening and closing of files
The file should be opened before reading and writing, and closed after use.
When writing a program, when opening a FILE, a pointer variable of FILE * will be returned to point to the FILE, which is equivalent to establishing the relationship between the pointer and the FILE.
ANSIC specifies that fopen function is used to open the file and fclose function is used to close the file.
FILE * fopen ( const char * filename, const char * node); int fclose ( FILE * stream );
The opening method is as follows:
How files are used | meaning | If the specified file does not exist |
---|---|---|
"r" (read only) | To enter data, open an existing text file | error |
"w" (write only) | To output data, open a text file | Create a new file |
"a" (added) | Add data to the end of a text file | error |
"rb" (read only) | To enter data, open a binary file | error |
"wb" (write only) | To output data, open a binary file | Create a new file |
"ab" (additional) | Add data to the end of a binary file | error |
"r +" (read / write) | To read and write, open a text file | error |
"w +" (read / write) | Create a new file for reading and writing | Create a new file |
"a +" (read / write) | Open a file and read and write at the end of the file | Create a new file |
"rb +" (read / write) | To read and write, open a binary file | error |
"wb +" (read / write) | Create a new binary file for reading and writing | Create a new file |
"ab +" (read / write) | Open a binary file and read and write at the end of the file | Create a new file |
1. Opening of files
(1) Code example
Example 2: open the test.txt file
#include<stdio.h> int main() { // Open the file test.txt // fopen("test.txt", "r"); // Equal path: // .. Indicates the upper level path // . indicates the current path // fopen("..\\test.txt","r"); // Absolute path: // fopen("D:\c language \ \ file operation explanation \ \ file operation explanation \ \ test.txt", "r"); // The symbol "\" may be escaped, so it takes the form of "\ \" FILE* pf = fopen("test.txt", "r"); return 0; }
(2) Illustration
(3) Error prone point
Similarly, the opening of a FILE may fail. When the opening fails, pf this pointer is NULL and the FILE structure is not created successfully. Therefore, we should judge whether the opening is successful.
for instance
Example 3:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { // Print error reason printf("%s\n", strerror(errno)); // strerror -> #include<string.h> // errno -> #include<errno.h> return 0; } return 0; }
Here we use strerror(errno) to print errors.
2. File closing
(1) Code example
Example 4:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { // Print error reason printf("%s\n", strerror(errno)); // strerror -> #include<string.h> // errno -> #include<errno.h> return 0; } else { // Open successfully fputs("fopen example", pf); // read file // Close file fclose(pf); pf = NULL; } return 0; }
The operation result is:
(successful) (when file exists)
No such file or directory
(2) Error prone point
For example 4: when fclose closes a file, it actually frees up the space in the file information area of the file, similar to the free function, but the pointer pf is not set to 0, so there is a risk.
So, for code
fclose(pf); pf = NULL;
Be sure to use it together.
7, Sequential reading and writing of files
function | Function name | Apply to |
---|---|---|
Character input function | fgetc | All input streams |
Character output function | fputc | All output streams |
Text line input function | fgets | All input streams |
Text line output function | fputs | All output streams |
Format input function | fscanf | All input streams |
Format output function | fprintf | All output streams |
Binary input | fread | file |
Binary output | fwrite | file |
1. About flow
Our usual operations are input and output from the keyboard to the screen, and the keyboard and screen here are external devices.
Keyboard - standard input device - stdin
Screen - standard output device - stdout
It is a program that opens two streaming devices by default
When the program runs, three streaming devices are opened by default:
stdin (type) - > file*
stdout (type) - > file*
stderr (type) - > file*
2. fputc function (write file)
Now that we have learned to create files and open files, how do we write files?
C language gives us a function: fputc to complete the operation of writing files. Its general form is as follows:
int fputc (int c, FILE *fp);
The operation is as follows:
Example 5:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { // Print error reason printf("%s\n", strerror(errno)); // strerror -> #include<string.h> // errno -> #include<errno.h> return 0; } // Write file fputc('x', pfWrite); fputc('i', pfWrite); fputc('y', pfWrite); fputc('o', pfWrite); fputc('u', pfWrite); // Close file fclose(pfWrite); pfWrite = NULL; return 0; }
Through example 5, we write "xiyou" into the file test.txt. Because the file test.txt did not exist before, when using "w", we will create a new file named "test.txt" and then write "xiyou".
When you open this file with Notepad, you will see the following:
In this way, we have completed a simple write file.
3. fgetc function (read file)
We have just learned how to write a file. After writing the file, how do we read it?
C language also gives us a function: fgetc to complete file reading. Its general form is as follows:
int fgetc(FILE *stream);
The operation is as follows:
Example 6:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { // Print error reason printf("%s\n", strerror(errno)); // strerror -> #include<string.h> // errno -> #include<errno.h> return 0; } // read file printf("%c", fgetc(pfRead)); printf("%c", fgetc(pfRead)); printf("%c", fgetc(pfRead)); printf("%c", fgetc(pfRead)); printf("%c", fgetc(pfRead)); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The running result is: xiyou
When reading a file, use fgetc(pfRead) multiple times without + 1. It defaults to the next bit.
4. Standard I / O streams for fgetc and fputc
Example 7:
#include<stdio.h> int main() { int a = fgetc(stdin); // Read information from standard input stream fputc(a, stdout); // Writes information to the standard output stream return 0; }
When we enter: h
The operation result is: h
Example 7 achieves a result of reading information from the keyboard and outputting it to the screen.
We have also used fgetc and fputc before. At that time, we input and output to the file, so these two functions are applicable to all input streams and output streams respectively.
5. fputs function (write file)
It is true that you can input files using the fputc function, but you can only write one character at a time. Can we write more?
C language provides us with fputs function to input text lines. Its general form is as follows:
int fputs(const char *str, FILE *stream);
Let's take an example:
Example 8:
#include<stdio.h> int main() { char buf[1024] = { 0 }; FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { return 0; } // Write file fputs("hello", pfWrite); fputs("world", pfWrite); // Close file fclose(pfWrite); pfWrite = NULL; return 0; }
Open the file test.txt with Notepad, as shown in the figure:
Some people will have questions about why there is no line break here. This is because we directly enter hello and world, which do not have "\ n". If we need line break, we need to add "\ n" ourselves.
6. fgets function (read file)
The fgetc function can indeed read files, but it can only read one character at a time. Can we read more?
C language provides us with fgets function to read text lines. Its general form is as follows:
char *fgets(char *str, int n, FILE *stream);
It is still the test.txt file. We open it with notepad and add hello, as shown in the figure:
Then read the text line:
Example 9:
#include<stdio.h> int main() { char buf[1024] = { 0 }; FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { return 0; } // read file fgets(buf, 1024, pfRead); printf("%s", buf); fgets(buf, 1024, pfRead); printf("%s", buf); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is:
hello
xiyou
be careful:
① When the "fgets(buf, 1024, pfRead);" operation here is used multiple times, like the fgetc function, it will automatically + 1 and read the next line.
② The code in the text is actually like this:
hello\n
xiyou\n
We also read "\ n" into buf when reading, so there is no need to wrap when printing.
7. Standard I / O streams for fgets and fputs
Example 10:
#include<stdio.h> int main() { // Read a line of text information from the keyboard char buf[1024] = { 0 }; fgets(buf, 1024, stdin);// Read from standard input stream fputs(buf, stdout);// Output to standard output stream return 0; }
When we enter: Hello World
The running result is helloworld
8. fprintf function (write file)
Only using fgetc and fgets functions is not enough to solve practical problems. When we encounter problems such as structures, we need to format them.
C language provides us with fprintf function to format the output. Its general form is as follows:
int fprintf (FILE* stream, const char*format[,argument] ...)
For example:
Example 11:
#include<stdio.h> struct S { int n; float score; char arr[10]; }; int main() { struct S s = { 1,99.5f,"xiyou" }; FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { return 0; } // Write files in formatted form fprintf(pfWrite, "%d %f %s", s.n, s.score, s.arr); // Close file fclose(pfWrite); pfWrite = NULL; return 0; }
Open the file test.txt with Notepad, as shown in the figure:
9. fscanf function (read file)
Only using fputc and fputs functions is not enough to solve practical problems. When we encounter problems such as structures, we need to format them.
C language provides us with fscanf function to format input. Its general form is as follows:
int fscanf(FILE *stream, char *format[,argument ...]);
For example:
Example 12:
#include<stdio.h> struct S { int n; float score; char arr[10]; }; int main() { struct S s = { 0 }; FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { return 0; } // Input file in formatted form fscanf(pfRead, "%d %f %s", &(s.n), &(s.score), s.arr); printf("%d %f %s\n", s.n, s.score, s.arr); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The running result is 1 99.500000 xiyou
10. Standard I / O streams for fscanf and fprintf
Example 13:
#include<stdio.h> struct S { int n; float score; char arr[10]; }; int main() { struct S s = { 0 }; fscanf(stdin, "%d %f %s", &(s.n), &(s.score), s.arr); fprintf(stdout, "%d %f %s", s.n, s.score, s.arr); return 0; }
When we enter: 1 99.5 xiyou
The running result is 1 99.500000 xiyou
11. Make a comparison
scanf/printf
Is a formatted input / output statement for a standard input stream / standard output stream
fscanf/fprintf
Is a formatted input / output statement for all input streams / all output streams
sscanf/sprintf
sscanf reads formatted data from a string
sprintf is to output (store) formatted data into a string
(1) sprintf function
Function: write formatted data into a string
General form:
int sprintf(char *string, char *format [,argument,...]);
for instance:
Example 14:
#include<stdio.h> struct S { int n; float score; char arr[10]; }; int main() { struct S s = { 1,99.5f,"xiyou" }; char buf[1024] = { 0 }; // Convert the formatted data into a string and store it in buf sprintf(buf, "%d %f %s", s.n, s.score, s.arr); printf("%s\n", buf); return 0; }
The running result is 1 99.500000 xiyou
**Note: * * the result here is in string form!
(2) sscanf function
Function: read the data in the formatted string.
General form:
int sscanf(char *buffer, char *format [,argument....]);
for instance:
Example 15:
#include<stdio.h> struct S { int n; float score; char arr[10]; }; int main() { struct S s = { 1,99.5f,"xiyou" }; struct S tmp = { 0 }; char buf[1024] = { 0 }; // Convert the formatted data into a string and store it in buf sprintf(buf, "%d %f %s", s.n, s.score, s.arr); // Read formatted data from buf to tmp sscanf(buf, "%d %f %s", &(tmp.n), &(tmp.score), tmp.arr); printf("%d %f %s\n", tmp.n, tmp.score, tmp.arr); return 0; }
The running result is 1 99.500000 xiyou
8, Random reading and writing of files
1. fseek function
Locate the file pointer according to the position and offset of the file pointer
Its general form is:
int fseek(FILE *stream, long offset, int origin);
The first parameter stream is the file pointer
The second parameter offset is the offset. A positive number indicates a positive offset and a negative number indicates a negative offset
The third parameter origin sets where to start the offset from the file. The possible values are:
SEEK_CUR, SEEK_END or SEEK_SET
SEEK_SET: beginning of file
SEEK_CUR: current position
SEEK_END: end of file
Let's take an example
Example 16:
#include<stdio.h> int main() { FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { return 0; } // 1. Write file fputs("welcome to xiyou", pfWrite); // Close file fclose(pfWrite); pfWrite = NULL; FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { return 0; } // 2. Locate file pointer fseek(pfRead, 2, SEEK_CUR); // 3. Read file int ch = fgetc(pfRead); printf("%c\n", ch); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is: l
Analysis: first, we use pfWrite to write the file, use the fgets function we learned before, and write "welcome to xiyou"; Then we use pfRead to read the file. First, we locate the file pointer through fseek. The current position is the starting position, i.e. "w", and the offset is 2, so we locate it to "l", then write the current character through fgetc we learned before, and finally print the read result of fgetc through the variable ch.
Note: when setting the offset of fseek() function, be careful not to exceed the range of the file!
2. ftell function
Returns the offset of the file pointer from the starting position
Its general form is:
long int ftell(FILE *stream);
Let's take an example:
Example 17:
#include<stdio.h> int main() { FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { return 0; } // 1. Write file fputs("welcome to xiyou", pfWrite); // Close file fclose(pfWrite); pfWrite = NULL; FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { return 0; } // 2. Locate file pointer fseek(pfRead, -2, SEEK_END); int pos = ftell(pfRead); printf("%d\n", pos); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is: 14
Analysis: first, we use pfWrite to write the file, use the fgets function we learned before, and write "welcome to xiyou"; Then we use pfRead to read the file, locate the file pointer through fseek, the end of the file is "o", the offset is - 2, and locate it to "y", and then use ftell function, its return value is "the offset of the file pointer from the starting position", which is also shaping, so print with the variable pos, and the offset of "y" from "w" is 14, so 14 is returned.
3. rewind function
Returns the position of the file pointer to the starting position of the file
Its general form is:
void rewind(FILE *stream);
Let's take an example:
Example 18:
#include<stdio.h> int main() { FILE* pfWrite = fopen("test.txt", "w"); if (pfWrite == NULL) { return 0; } // 1. Write file fputs("welcome to xiyou", pfWrite); // Close file fclose(pfWrite); pfWrite = NULL; FILE* pfRead = fopen("test.txt", "r"); if (pfRead == NULL) { return 0; } // 2. Locate file pointer fseek(pfRead, 9, SEEK_CUR); // Print int ch = fgetc(pfRead); printf("%c\n", ch); // Return to starting position rewind(pfRead); // Print ch = fgetc(pfRead); printf("%c\n", ch); // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is:
o
w
Analysis: we first locate "o", read the print, and then use the rewind function to read the print again. It is found that the result of the second print is "w", indicating that rewind makes the current position of the file pointer the starting position.
9, Determination of end of document
1. How to print errors
(1) strerror function
In the dynamic memory allocation module, we learned the strerror function to the string address of the error information corresponding to the error code.
For example:
Example 19:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE* pfRead = fopen("test2.txt", "r"); if (pfRead == NULL) { // Print error reason printf("%s\n", strerror(errno)); // strerror -> #include<string.h> // errno -> #include<errno.h> return 0; } // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is:
No such file or directory
Analysis: the file "test2.txt" does not exist here, but we use "r" to read it. As mentioned earlier, this will report an error,
(2) perror function
Today we're going to learn another way to print errors - the perror function
Its general form is:
void perror(const char *str)
The use of the perror function is very simple. It does not need to reference so many header files like the strerror function. It is contained in "stdio.h", and it does not need to pass an error code.
The output form of perror function is:
str: error reason
Let's take an example:
Example 20:
#include<stdio.h> int main() { FILE* pfRead = fopen("test2.txt", "r"); if (pfRead == NULL) { perror("File opening failed"); return 0; } // Close file fclose(pfRead); pfRead = NULL; return 0; }
The operation result is:
File opening failed: No such file or directory
2. feof function
Detect the file terminator on the stream. If the file ends, a non-0 value is returned; otherwise, 0 is returned
Its general form is:
int feof(FILE *stream);
(1) Text file
Take an example
Example 21:
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("File opening failed"); return 0; } // read file int ch; // Note: int, not char, requires EOF processing // fgetc will return EOF when reading fails or when the file ends while ((ch = fgetc(pf)) != EOF) // Standard C I/O read file cycle { putchar(ch); } printf("\n"); //Judge why it ended if (ferror(pf)) { printf("I/O error when reading\n"); } else if (feof(pf)) { printf("End of file reached successfully\n"); } // Close file fclose(pf); pf = NULL; return 0; }
The operation result is:
welcome to xiyou
End of file reached successfully
(2) Binary file
Let's take an example
Example 22:
#include<stdio.h> enum { SIZE = 5 }; int main() { double a[SIZE] = { 1.0,2.0,3.0,4.0,5.0 }; double b = 0.0; size_t ret_code = 0; FILE* pf = fopen("test.bin", "wb"); // wb - binary form if (pf == NULL) { perror("File opening failed"); return 0; } // Write an array of double fwrite(a, sizeof(*a), SIZE, pf); fclose(pf); pf = fopen("test.bin", "rb"); // rb - binary form // Read array of double while ((ret_code = fread(&b, sizeof(double), 1, pf)) >= 1) { printf("%lf\n", b); } //Judge why it ended if (ferror(pf)) { printf("Error reading test.bin\n"); } else if (feof(pf)) { printf("Error reading test.bin: unexpected end of file\n"); } // Close file fclose(pf); pf = NULL; return 0; }
The operation result is:
1.000000
2.000000
3.000000
4.000000
5.000000
Error reading test.bin: unexpected end of file
(3) Summary of error prone points
Misused feof
Note: during text reading, the return value of the feof function cannot be directly used to judge whether the file is finished! It is used to judge whether the reading fails or the end of the file is encountered when the file reading ends.
① Whether the text file reading is completed, and judge whether the return value is EOF (fgetc) or NULL (fgets)
For example:
I. fgetc determines whether the return value is EOF.
II. fgets determines whether the return value is NULL.
② Judge whether the return value is less than the actual number to be read.
For example:
fread determines whether the return value is less than the actual number to be read.
summary
This is the end of the relevant knowledge about file operation. There are still many concepts. We need to study hard. We must not be eager for quick success and instant benefits. We need to learn step by step. I believe everyone who can see here is certainly willing to do so. See you in the next issue. Come on!