The author is Xie Enming, an official account of Programmer Union (wechat id: CoderHub). Please indicate the source of reprint. Original: www.jianshu.com/p/4adb95073…

The whole series of C language Exploration

Content abstract


  1. preface
  2. File opening and closing
  3. Different ways to read and write files
  4. Move in a file
  5. File renaming and deletion
  6. Part 2 Lesson 8 Notice

1. Introduction


On a class trip to the C language to explore | part 2 class 6: create your own variable types Later, we are going to learn common file reading and writing.

We’ve learned so much about variables that we already know that variables are really powerful and can help us achieve a lot of things.

As powerful as variables are, they have drawbacks, the biggest of which is that they don’t last forever.

Because C variables are stored in memory, they are cleared when your program exits and cannot be retrieved the next time the program starts.

“Suddenly look back, that man is not in the dim lights…”

“How can you and I today repeat the story of yesterday? Can this old ticket still board your miserable ship?”

Can’t ah, “waves can’t still” ah…

If so, how do we save the highest score in a game written in C? How to write a text editor in C that saves text on exit?

Fortunately, in C we can read and write files. These files will be stored on our computer’s hard drive, so they won’t be wiped when the program exits or the computer shuts down.

To implement file reading and writing, we’ll use everything we’ve learned so far:

Pointers, structs, strings, and so on.

It’s kind of a review.

2. Open and close files


To read and write files, we need to use some functions, structures, and so on defined in the stdio.h library header.

Yes, known as stdio.h, our “old friends” printf and scanf functions are also defined in this header file.

Here is a sequence of steps we must follow to open a file for reading or writing:

  1. Call the “file open” function fopen (f is the first letter of file; Open) returns a pointer to the file.

  2. Check whether the file opened successfully by checking the return value of fopen in step 1 (the file pointer). If the pointer is NULL, the opening fails and we need to stop the operation and return an error.

  3. If the file is opened successfully (the pointer is not NULL), then we can continue to read and write the file using the function in stdio.h.

  4. Once we have finished reading and writing, we close the file using the fclose function.

First we’ll learn how to use the fopen and fclose functions, and then we’ll learn how to read and write files.

Fopen: opens a file


The prototype of the function fopen looks like this:

FILE* fopen(const char* fileName, const char* openMode);
Copy the code

As you can see, this function takes two arguments:

  • FileName: indicates the fileName (name indicates the name). Is a string type and is const, meaning its value cannot be changed.

  • OpenMode: indicates the opening mode (open: “open”, mode: “mode”). An indicator of what to do when we open the file. Read only, write only, read and write, etc.

The return value of this function is FILE *, which is a pointer to FILE.

FILE is defined in stdio.h. Interested readers can find the definition of FILE for themselves.

We give a general definition of FILE:

typedef struct {
    char *fpos; /* Current position of file pointer (absolute address) */
    void *base; /* Pointer to the base of the file */
    unsigned short handle; /* File handle */
    short flags; /* Flags (see FileFlags) */
    short unget; /* 1-byte buffer for ungetc (b15=1 if non-empty) */
    unsigned long alloc; /* Number of currently allocated bytes for the file */
    unsigned short buffincrement; /* Number of bytes allocated at once */
} FILE;
Copy the code

You can see that FILE is a struct with seven variables in it. Of course, we don’t have to go into the definition of FILE, as long as we know how to use FILE, and different operating systems have different definitions of FILE.

Careful readers may be asking: “Why is the FILE structure capitalized?” How can they be named the same way as constants?”

Good question. The naming we suggested earlier (capitalizing structures, for example: StructName) is just a “specification” (though most programmers like to follow it), not a requirement.

It just goes to show that the predecessors who wrote Stdio.h didn’t necessarily follow this “specification.” Of course, it doesn’t matter to us.

There are several openmodes available:

  • R: Read only. R is the first letter of read. In this mode, we can only read files, not write to them. The file must already exist.

  • W: Just write. W is the first letter of write. In this mode, the contents of the file can only be written, but not read. If the file does not exist, it will be created.

  • A: Additional. A is the first letter of append. In this mode, write from the end of the file. If the file does not exist, it will be created.

  • R + : Reading and writing. In this mode, files can be read and written, but the files must already exist.

  • W + : Reading and writing. The file content is deleted in advance. In this mode, if the file exists and the content is not empty, the content is emptied first. If the file does not exist, it will be created.

  • A + : read/write append. In this mode, files are read and written from the end of the file. If the file does not exist, it will be created.

The patterns listed above can also be combined with the pattern B. A binary B For each of the above modes, if you add b, it will become rb, wb, ab, rb+, WB +, ab+) and the file will be opened in binary mode. But binary patterns are generally not that common.

In general, r, w and r+ are used a lot. The W + mode should be used with caution because it clears the file contents first. Mode A is useful when you need to add content to a file.

Here is an example program that opens a file in r+ (read/write) mode:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;

    file = fopen("test.txt"."r+");

    return 0;
}
Copy the code

Thus, file becomes a pointer to the test.txt file.

“Where is our test.txt file?” you ask.

TXT files are in the same directory as executable files.

“Do files have to end in.txt?”

No, it’s up to you to determine the file’s suffix. You can create a file called xxx.level to record the level information in your game.

“Do files have to be in the same folder as executable files?”

Also is not. It can be located in any folder on the current system, as long as you specify the path of the file in the file name parameter of the fopen function, for example:

file = fopen("folder/test.txt"."w");
Copy the code

In this case, the test. TXT file is located in the folder of the current directory. The folder/test.txt is called “relative path”.

We can also do this:

file = fopen("/home/user/folder/test.txt"."w");
Copy the code

The/home/user/folder/test. TXT is “absolute path”.

Test open file


After calling the fopen function to try to open the file, we need to check the return value of fopen to see if the file was opened successfully.

The detection is simple: if fopen returns NULL, the opening fails; If the value is not NULL, the opening is successful. The following is an example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;

    file = fopen("test.txt"."r+");

    if(file ! = NULL) {// write file}else{// Displays an error messageprintf("Unable to open test. TXT file \n");
    }

    return 0;
}
Copy the code

Remember to check the return value every time you use the fopen function, because if the file does not exist or is being used by another program, it may cause the current program to fail.

Fclose: closes the file


Close: Close.

If we successfully open a file, we can read and write to the file (more on this in the next section).

If we are done with the file, we should close the file to free up the occupied file pointer.

To close the file, we need to call the fclose function, which frees memory, that is, deletes your file (pointer) from memory.

Function prototype:

int fclose(FILE* pointerOnFile);
Copy the code

This function takes only one argument: a pointer to a file.

The return value of a function (int) has two cases:

  • 0: indicates that the shutdown is successful.
  • EOF (short for End Of File, “End Of File.” Generally equal to -1) : If the shutdown fails.

The following is an example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;

    file = fopen("test.txt"."r+");

    if(file ! = NULL) {// Write files //... fclose(file); // Close the file we opened earlier}return 0;
}
Copy the code

3. Different ways to read and write files


Now that we know how to open and close files, let’s learn how to read and write files.

We’ll first learn how to write to a file (which is easier than reading), and then we’ll look at how to read from a file.

Write to a file

There are several functions for writing files, and we can choose the most appropriate function to use depending on the situation.

Let’s learn three functions for writing files:

  • Fputc: Writes one character (one at a time) to a file. File put character is short for file Put character. A. put B. character C. character D. character

  • Fputs: Writes a string to a file. File Put String. String indicates a string.

  • Fprintf: Writes a formatted string to a file, almost the same as printf, but with a pointer to the file.

fputc

This function is used to write one character at a time to a file.

Function prototype:

int fputc(int character, FILE* pointerOnFile);
Copy the code

This function takes two arguments:

  • Character: int variable, indicating the character to be written. Or we could just write ‘A’, just like we did in the ASCII video.

  • PointerOnFile: Pointer to a file.

The function returns an int. If the write fails, EOF; Otherwise, it will be a different value.

Example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;

    file = fopen("test.txt"."w");

    if(file ! = NULL) { fputc('A', file); // Write the character A fclose(file); }return 0;
}
Copy the code

The above program is used to write the character ‘A’ to the test.txt file.

fputs

This function is similar to fpuTC except that FpuTC writes one character at a time and fputs writes one string at a time.

Function prototype:

int fputs(const char* string, FILE* pointerOnFile);
Copy the code

Similarly, this function takes two arguments:

  • String: The string to be written.

  • PointerOnFile: Pointer to a file.

If there is an error, the function returns EOF; Otherwise, return a value different from EOF.

Example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;

    file = fopen("test.txt"."w");

    if(file ! = NULL) { fputs("Hello, friend. \n How are you?", file);
        fclose(file);
    }

    return 0;
}
Copy the code
fprintf

This function is useful because not only can it write a string to a file, but the string can be formatted by us. The use of printf is similar to that of printf, except that there is a file pointer.

Function prototype:

int fprintf(FILE *stream, const char *format, ...)
Copy the code

Example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;
    int age = 0;

    file = fopen("test.txt"."w");

    if(file ! = NULL) {// Ask the age of the userprintf("How old are you? ");
        scanf("%d", &age); // Write to fprintf(file,"User age is %d years \n", age);
        fclose(file);
    }

    return 0;
}
Copy the code

Read from the file


We can use a function with the same name as the one we used to write to the file, only slightly modified, but also three:

  • Fgetc: Read a character. File Get Character is short for File Get character. Get, get, get

  • Fgets: Reads a string. File Get String.

  • Fscanf: Similar to scanf, but with a pointer to a file. Scanf reads from user input, while fscanf reads from a file.

This time we’re going to go through these three functions a little bit more briefly, because if you know the first three writing functions, then the three reading functions are similar. It’s just the opposite.

fgetc

Firstly, the function prototype is given:

int fgetc(FILE* pointerOnFile);
Copy the code

The return value of the function is the character read. If the character cannot be read, EOF is returned.

But how do we know where we’re reading from in the file? Is it the third character or the tenth character?

Actually, when we read a file, we have a cursor that moves with it.

This is of course a virtual cursor, you won’t see it on the screen. You can imagine this cursor is similar to the blinking cursor you see when you edit a file with notepad. This cursor indicates your current position in the file.

In later sections, we will learn how to move the cursor to a specific location in the file. It can be at the beginning or at the seventh character.

Each time the fgeTC function reads a character, the cursor moves one character length. We can use a loop to read all the characters in the file. Such as:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE* file = NULL;
    int currentCharacter = 0;

    file = fopen("test.txt"."r");

    if(file ! = NULL) {// loop reading, one character at a timedo{ currentCharacter = fgetc(file); // Read a characterprintf("%c", currentCharacter); // Display the read character}while(currentCharacter ! = EOF); // We continue until fgeTC returns EOF fclose(file); }return 0;
}
Copy the code
fgets

This function reads one string at a time so that it doesn’t have to read one character at a time (which is sometimes inefficient).

This function reads at most one line at a time, because it finishes reading when it encounters the first ‘\n’ (newline character). So if we want to read multiple rows, we need to loop.

Insert a little carriage return and line feed knowledge: About the origins and differences between carriage return and line feed. Before computers, there was something called a Teletype Model 33 that could type 10 characters per second. One problem is that it takes 0.2 seconds to type a newline, which is exactly two characters. If a new character is passed within 0.2 seconds, the character will be lost. So the developers figured out a way to solve this problem by adding two ending characters to each line. One, called a carriage return, tells the typewriter to position the print head on the left edge. The other, called “line break,” tells the typewriter to move the paper down one line. This is where “line feed” and “carriage return” come from, as can be seen from their English names. Later, computers were invented, and the two concepts were transferred to computers. Back then, memory was expensive, and some scientists thought it was wasteful to add two characters to the end of each line; just one would do. So, there was a disagreement. On Unix/Linux, each line ends with only “< newline >”, i.e. “\n”; On Windows, each line ends with “< newline >< carriage return >”, i.e. “\n\r”; On macOS, each line ends with “< enter >”, or “\r”. As a direct consequence, files on Unix/Linux/macOS open on Windows and all the text becomes a single line. Windows files opened on Unix/Linux/macOS may have an extra ^M symbol at the end of each line. In Linux, the “return + line” operation is performed when a newline character is encountered. Instead, the return character is only displayed as a control character and no return operation occurs. Windows to “return + line” will achieve “return + line”, the lack of a control character or the order is not correct to start another line.

Function prototype:

char* fgets(char* string, int characterNumberToRead, FILE* pointerOnFile);
Copy the code

Example:

#include <stdio.h>

#define MAX_SIZE 1000

int main(int argc, char *argv[])
{
    FILE* file = NULL;
    char string[MAX_SIZE] = ""; // Array of MAX_SIZE, initially empty file = fopen("test.txt"."r");

    if(file ! = NULL) { fgets(string, MAX_SIZE, file); // We read a string of up to MAX_SIZE characters and store it in a stringprintf("%s\n", string); // Display the string fclose(file); }return 0;
}
Copy the code

Here, our MAX_SIZE is large enough (1000) to hold the number of characters on the next line. So we stop reading when we hit ‘\n’, so all this code does is read a line from the file and print it out.

So how can we read the entire file? It’s easy. Add a loop.

As follows:

#include <stdio.h>

#define MAX_SIZE 1000

int main(int argc, char *argv[])
{
    FILE* file = NULL;
    char string[MAX_SIZE] = ""; // Array of MAX_SIZE, initially empty file = fopen("test.txt"."r");

    if(file ! = NULL) {while(fgets(string, MAX_SIZE, file) ! = NULL) // We read the contents of the file line by line, as long as we do not encounter the end of the fileprintf("%s\n", string); // Display the string fclose(file); }return 0;
}
Copy the code
fscanf

The principle of this function is the same as scanf. Responsible for reading the content of a specified style from a file.

Function prototype:

int fscanf(FILE *stream, const char *format, ...)
Copy the code

Example:

For example, create a test. TXT file and enter three numbers: 23, 45, 67.

The input can be of the following form:

  • There are Spaces between each number

  • I’m going to switch lines between each number

#include <stdio.h>int main(int argc, char *argv[]) { FILE* file = NULL; int score[3] = {0}; // Array of 3 best scores file = fopen("test.txt"."r");

    if(file ! = NULL) { fscanf(file,"%d %d %d", &score[0], &score[1], &score[2]);
        printf("The best scores are: %d, %d and %d\n", score[0], score[1], score[2]);

        fclose(file);
    }

    return 0;
}
Copy the code

Run output:

Best scores: 23, 45, 67Copy the code

4. Move the file


Earlier we mentioned the virtual “cursor”, now let’s take a closer look at it.

Every time we open a file, there is actually a “cursor” that identifies where you are in the file.

Think of it like a text editor. Every time you type in a text editor (like Notepad), doesn’t it have a cursor that moves around? It tells you where you are in the file, where your next input will start.

In summary, the cursor system allows us to read and write files at specified locations.

We introduce three functions related to cursor movement in files:

  • Ftell: Indicates where it is in the file. Tell b. tell C. tell D. tell

  • Fseek: Moves a cursor in a file to a specified position. Seek d. seek

  • Rewind: Rewinds the cursor to the start of the file (this is the same effect as using fseek to return the cursor to the start of the file). Rewind means “turn back”.

Ftell: Indicates the cursor position currently in the file


This function is very simple to use. It returns an integer value of type long indicating the current cursor position. The function prototype is:

long ftell(FILE* pointerOnFile);
Copy the code

The pointerOnFile pointer is a file pointer to the current file.

I’m sure you know how to use it without using an example.

Fseek: Moves the cursor to the specified position


The function prototype is:

int fseek(FILE* pointerOnFile, long move, int origin);
Copy the code

This function enables the cursor to move from position (origin) in a file (pointerOnFile pointer). Origin starts to move some distance. Move = move

  • The move argument: can be a positive integer, indicating forward movement; 0 indicates no movement. Or a negative integer, indicating a rollback.

  • The origin argument: It can take any of the following values (constants defined by #define) :

    • SEEK_SET: at the beginning of the file. SET indicates setting.
    • SEEK_CUR: Cursor current location. CUR is an abbreviation for current.
    • SEEK_END: End of the file. END = END

Let’s take a look at some examples:

// This line of code places the cursor 5 positions from the beginning of the file fseek(file, 5, SEEK_SET); Fseek (file, -3, SEEK_CUR); fseek(file, -3, SEEK_CUR); // This line of code places the cursor at the end of the file fseek(file, 0, SEEK_END);Copy the code

Rewind: Returns the cursor to the start of the file


This function is equivalent to using fseek to return the cursor to zero

void rewind(FILE* pointerOnFile);
Copy the code

I believe that it is difficult to use it, look at the function prototype at a glance. And fseek (file, 0, SEEK_SET); It’s an effect.

5. Rename and delete files


Let’s finish this lesson by learning two simple functions:

  • The rename function renames a file (rename means “rename”).

  • Remove function: deletes a file (remove stands for “remove”).

What makes these two functions special is that, unlike previous file-manipulation functions, they don’t need a file pointer as an argument, just pass the file name to them.

Rename: renames a file


Function prototype:

int rename(const char* oldName, const char* newName);
Copy the code

OldName is the “oldName” of the file and newName is the “newName” of the file.

If the function executed successfully, 0 is returned; Otherwise, return a non-zero int.

Here’s an example in use:

int main(int argc, char *argv[])
{
      rename("test.txt"."renamed_test.txt");

      return 0;
}
Copy the code

That’s easy.

Remove: deletes a file


Function prototype:

int remove(const char* fileToRemove);
Copy the code

FileToRemove is the name of the file to be removed.

Note: The remove function should be used with caution, as it does not prompt you to confirm deleting files. Files are permanently deleted directly from the hard drive and are not moved to the trash first. Retrieving deleted files requires special software, but the process may not be easy or successful.

Example:

int main(int argc, char *argv[])
{
    remove("test.txt");

    return 0;
}
Copy the code

6. Part 2 Lesson 8 Notice


That’s all for today’s lesson, come on!

Next lesson: the second part C language exploration trip | lesson 8: dynamic allocation


My name is Xie Enming, the operator of the public account “Programmer Union” (wechat id: CoderHub), the moOCs elite lecturer Oscar, and a lifelong learner. Love life, like swimming, a little cooking. Life motto: “Run straight for the pole”