1 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, the status of the file and the current location of the file, etc.). This information is stored in a structure variable. The structure type is declared by the system, named FILE
.
For example, the header file provided by the VS2013 compilation environment stdio.h
has the following file type declaration:
struct _iobuf{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
The contents of the FILE type of different C compilers are not exactly the same, but they are similar.
Whenever a file is opened, the system will automatically create a variable of the FILE structure according to the situation of the file, and fill in the information, and the user does not need to care about the details.
Generally, the variables of this FILE structure are maintained through a FILE pointer, which is more convenient to use.
Below we can create a FILE* pointer variable:
FILE* pf; //文件指针变量
Define pf as a pointer variable pointing to FILE type data. You can make pf point to the file information area of a certain file (it is a structure variable). The file can be accessed through the information in the file information area. That is to say, the file associated with it can be found through the file pointer variable .
2 Opening and closing of files
Files should be opened before reading and writing, and should be closed after use.
When writing a program, when opening a 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.
fopen
Function: used to open the file.
Function prototype:
FILE *fopen(const char *filename, const char *mode);
filename
The argument is the name of the file to open, which can be a relative path or an absolute path.mode
The parameter specifies the mode to open the file, with the following options:
open method | describe | if the file does not exist |
---|---|---|
“r” | Open an existing text file in read-only mode . | Opening fails and returns a NULL pointer. |
“w” | Write mode , truncates the file to 0 bytes if the file exists, creates a new file if it does not exist. | Create a new empty file. |
“a” | Append mode , used to append data at the end of the file, or create a new file if it does not exist. | Create a new empty file. |
“rb” | Open the file in binary read-only mode . | Opening fails and returns a NULL pointer. |
“wb” | Open the file in binary write mode , truncate the file to 0 bytes if it exists, or create a new file if it does not exist. | Create a new empty file. |
“ab” | Open the file in binary append mode , or create a new file if it does not exist. | Create a new empty file. |
“r+” | Read-write mode, opens an existing text file, allowing reading and writing. | Opening fails and returns a NULL pointer. |
“w+” | Read-write mode, if the file exists, truncate the file to 0 bytes, if the file does not exist, create a new file, allowing reading and writing. | Create a new empty file. |
“a+” | Read-write mode for appending data at the end of the file, creating a new file if the file does not exist, allowing reading and writing. | Create a new empty file. |
“r+b” | Read-write mode, opens an existing file in binary mode, allowing reading and writing. | Opening fails and returns a NULL pointer. |
“w+b” | Read-write mode, open the file in binary mode, truncate the file to 0 bytes if the file exists, create a new file if the file does not exist, allow reading and writing. | Create a new empty file. |
“a+b” | Read-write mode, appends data at the end of the file in binary, creates a new file if the file does not exist, allows reading and writing. | Create a new empty file. |
fclose
Function: used to close the file.
Function prototype:
int fclose(FILE *stream);
stream
The argument is a pointerfopen
to the type returned by the functionFILE
that identifies the file to close.
Example:
#include <stdio.h>
int main() {
FILE *pf1 = fopen("HelloWorld.text", "w"); //打开失败返回空指针 在工程下创建文件(相对路径)
FILE *pf2 = fopen("c:\\code\\1.text", "w");//打开失败返回空指针 在C盘创建文件(绝对路径)
if (pf1 == NULL) {
perror("fopen pf1");
return 1;
}
if (pf2 == NULL) {
perror("fopen pf2");
return 1;
}
//关闭文件
fclose(pf1);
fclose(pf2);
pf1 = NULL;
pf2 = NULL;
return 0;
}
// 输出结果:fopen pf2: No such file or directory C盘没有该目录
3. Sequential reading and writing of files
3.1 fgetc and fputc
fgetc
and fputc
standard library functions for file reading and writing. They are used to read a single character from a file and write a single character to a file.
fgetc
function:
int fgetc(FILE *stream);
fgetc
Function to read a character from the given file stream (represented by FILE
a pointer ). stream
It returns the characters read, and if the end of file was read or an error occurred, it returns EOF
(End of File).
Example usage:
#include <stdio.h>
int main() {
//打开文件
FILE *pf = fopen("text.text", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
//写文件
int i = 0;
for (i = 0; i < 26; i++) {
fputc('a' + i, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fputc
function:
int fputc(int character, FILE *stream);
fputc
Function to write a character to the specified file stream (represented by FILE
a pointer stream
). It returns the character written, or if there was an error writing it EOF
.
Example usage:
#include <stdio.h>
int main() {
//打开文件
FILE *pf = fopen("text.text", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
//读文件
int ch = 0;
while ((ch = fgetc(pf)) != EOF) {
printf("%c", ch);
}
//每次读取后 pf指针会自动加1
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fgetc
and fputc
are lower-level file I/O functions. C language also provides more advanced file read and write functions, such as fgets
and fputs
, which are more convenient for reading and writing strings and a line of text.
3.2 fgets and fputs
fgets
and are standard library functions for reading and writing files, which are more convenient for reading and writing strings and a line of text fputs
than and fgetc
.fputc
fgets
function:
char *fgets(char *str, int n, FILE *stream);
fgets
Function to read a line of text from the given file stream (represented by FILE
a pointer stream
) and store it in a character array str
. The parameter n
specifies the maximum number of characters to read (including newline and terminator) to avoid buffer overflow. The text read will contain newlines (if present) and be \0
terminated by a null character.
Example usage:
#include <stdio.h>
int main() {
//打开文件
FILE *pf = fopen("text.text", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
//写文件
fputs("hello ", pf);
fputs("World", pf);
//如果文件本身有内容 就会清楚原来文件的所有内容 在写入
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fputs
function:
int fputs(const char *str, FILE *stream);
fputs
Function to write a null- \0
terminated string str
to the specified file stream (represented by FILE
a pointer stream
). It doesn't add an extra newline at the end of the string, so it needs to be added manually.
Example usage:
#include <stdio.h>
int main() {
//打开文件
FILE *pf = fopen("text.text", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
//读文件一行一行读 只读一行
char arr[20] = "#########";
fgets(arr, 20, pf);
printf("%s", arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
As with the previous fgetc
and fputc
, fgets
the and fputs
should also do error checking and close the file when the file pointer is no longer needed. They are more suitable for reading and writing text files and lines of text, and can easily read a line of text or write a string with a newline character.
3.3 fprintf and fscanf
fscanf
and fprintf
are standard library functions for formatted file input and output. They are similar to scanf
functions printf
, but can read data from and write data to files.
fprintf
function:
int fprintf(FILE *stream, const char *format, ...);
fprintf
The function is used to write the formatted data format
to the given file stream (represented by FILE
the pointer stream
) according to the specified format string. format
The argument is a format string, similar to printf
format strings in functions. According to the format specifiers in the format string, fprintf
the corresponding data is converted to a string and written to the file.
Example:
#include <stdio.h>
struct S {
char name[20];
int age;
float score;
};
int main() {
struct S s = {
"zhangsan", 20, 95.5f};
//把s中的结构体数据写到文件中
FILE *pf = fopen("test.text", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
//写文件
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
return 0;
}
fscanf
function:
int fscanf(FILE *stream, const char *format, ...);
fscanf
The function is used to read data according to the specified format string from the given file stream (represented by FILE
pointer ). The read data will be parsed according to the format specifiers in the format string, and the parsed values will be stored in the corresponding variables. The argument is a format string, similar to format strings in functions.stream
format
format
scanf
Example usage:
#include <stdio.h>
struct S {
char name[20];
int age;
float score;
};
int main() {
struct S s = {
0};
//把s中的结构体数据读出来
FILE *pf = fopen("test.text", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
//读文件
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
printf("%s %d %f", s.name, s.age, s.score);
return 0;
}
Like the previous functions, fscanf
and fprintf
should also do error checking and close the file when the file pointer is not needed. They make it easier to read and write data in files, and enable data input and output in a specified format.
3.4 fwrite and fread
fread
and fwrite
are standard library functions for binary file reading and writing. Unlike the previous functions such as fgets
, fputs
, fscanf
and fprintf
, these two functions work with binary data rather than text data. They are typically used to read and write binary files or blocks of binary data.
fwrite
function:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite
The function ptr
writes data from the memory region pointed to by the given file stream (represented by FILE
the pointer stream
). The parameter size
represents the number of bytes per data item, while nmemb
the number of data items to write. The function returns the actual number of data items written successfully, which should usually be nmemb
compared with to ensure that all data was written correctly.
Example:
#include <stdio.h>
struct S {
char name[20];
int age;
float score;
};
int main() {
struct S s = {
"zhangsan", 20, 95.5f};
//把s中的结构体数据写到文件中
FILE *pf = fopen("test.text", "wb");
if (pf == NULL) {
perror("fopen");
return 1;
}
//写文件
fwrite(&s, sizeof(s), 1, pf);
return 0;
}
fread
function:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread
The function reads data from the given file stream (represented by FILE
a pointer stream
) and stores it in ptr
the memory area pointed to by . The parameter size
represents the number of bytes per data item, while nmemb
the number of data items to read. The function returns the actual number of data items successfully read, which should usually be nmemb
compared with to ensure that all data was read correctly.
Example:
#include <stdio.h>
struct S {
char name[20];
int age;
float score;
};
int main() {
struct S s = {
0};
//把s中的结构体数据读到文件中
FILE *pf = fopen("test.text", "rb");
if (pf == NULL) {
perror("fopen");
return 1;
}
//读文件
fread(&s, sizeof(s), 1, pf);
printf("%s %d %f\n", s.name, s.age, s.score);
return 0;
}
fread
and fwrite
are lower-level file I/O functions that work directly with binary data. When using them, you need to pay attention to the byte order and data storage format to avoid problems in different platforms or environments. If you need to deal with complex data structures or require more advanced serialization functions, it is recommended to use specialized libraries or functions for processing.
For any C program, as long as it is running, three streams are opened by default
stdin
- standard input stream - keyboard
stdout
- stdout stream - screen
stderr
- stderr stream - screenTheir types are both FILE*
int main()
{
int ch = fgetc(stdin); //键盘输入d
fputc(ch,stdout); //d键盘输出d
return 0;
}
//printf == fprintf( ,stdout)
4. Compare a set of functions sprintf/sscanf
scanf
: Enter data from the keyboard according to a certain format
printf
: Print (output) the data to the screen according to a certain format
Formatted input/output statements for standard input/output streams
fscanf
: Input data from the input stream (file/stdin) according to a certain format
fprintf
: Output data to the output stream (file/stdout) according to a certain format
Formatted I/O statements for all I/O streams
sscanf
int sscanf(const char *s,const char* format,...)
Read formatted data from a string according to a certain format
sprintf
int sprintf(char * str,const char* format,...)
Convert the formatted data into a string according to a certain format
Example:
#include <stdio.h>
struct S {
char name[10];
int age;
float score;
};
int main() {
char buf[100] = {
0};
struct S tmp = {
0};
struct S s = {
"zhangsan", 20, 95.5f};
//能否把这个结构体的数据转换成字符串
//转换成 - zhangsan 20 95.5
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);
//能否将buf中的字符串还原成一个结构体数据
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);//以结构体的形式打印
return 0;
}
5. Random reading and writing of files
5.1 fseek
fseek()
Allows moving the file position indicator to a specific position in the file. This function is especially useful when you want to start reading or writing data from a specific position in the file rather than from the beginning.
fseek()
The prototype of the function is as follows:
int fseek(FILE *stream, long int offset, int origin);
parameter:
stream
FILE
: Pointer to the object representing the file you want to operate on . The file must have been opened in a mode that supports positioning, such as reading or writing.offset
: The number of bytes of the file position indicator to move. Positive values move the indicator forward, negative values move the indicator backward. Usually oflong int
type.origin
: The reference position from which to apply the offset. It can take one of the following values:SEEK_SET
(0): The offset is relative to the beginning of the file.SEEK_CUR
(1): The offset is relative to the position of the current file pointer.SEEK_END
(2): The offset is relative to the end of the file.
return value:
fseek()
Returns 0 if successful .- If an error occurs, it returns a non-zero value, indicating failure.
Example:
#include <stdio.h>
int main()
{
FILE *pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen()");
return 1;
}
//读文件 - 文件中存放abcdef
int ch = fgetc(pf);
printf("%c\n", ch); // 输出:a 读完后文件指针往后+1 指向b
fseek(pf, 2, SEEK_CUR); //从当前文件指针往后偏移2个 (当前是b)
ch = fgetc(pf);
printf("%c\n", ch); // 输出:d
fseek(pf, 3, SEEK_SET); //文件首部向后偏移三个
ch = fgetc(pf);
printf("%c\n", ch); // 输出:d
fseek(pf, -3, SEEK_END); //文件首部向前偏移三个 末尾的指针是f的后面
ch = fgetc(pf);
printf("%c\n", ch); // 输出:d
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fseek()
Should be used for binary files or text files where you know the exact byte offset to move to. For text files, using fseek()
may cause unexpected results due to differences in newline characters on different platforms. In this case, it is better to fgets()
read the file line by line, or process the file contents character by character.
5.2 ftell
ftell()
The offset within the file used to get the current file position indicator (that is, the position of the file pointer). It returns the number of bytes at the current position relative to the beginning of the file.
The function prototype is:
long int ftell(FILE *stream);
parameter:
stream
: AFILE
pointer to the object, indicating that you want to get the file at the current location.
return value:
- On success,
ftell()
returns along int
value of type representing the offset, in bytes, of the current file position indicator from the beginning of the file. - If an error occurs, the return value
EOF
(usually -1) indicates that an error occurred.
#include <stdio.h>
int main() {
FILE *pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen()");
return 1;
}
//读文件 - 文件中存放abcdef
int ch = fgetc(pf);
printf("%c\n", ch);// 输出:a 读完后文件指针往后+1 指向b
fseek(pf, 2, SEEK_CUR);//从当前文件指针往后偏移2个 (当前是b)
ch = fgetc(pf);
printf("%c\n", ch);// 输出:d
int pos = ftell(pf);//当文件指向d的时候 文件指针往后偏移1个 指向了e
printf("%d\n", pos);//输出:4 e和a相差4个位置
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
ftell()
Typically fseek()
used with to determine the offset of a specific location within a file. Positioning the file pointer to this location can be achieved by first calling ftell()
to get the current location, and then using this value as fseek()
the offset of the .
5.3 rewind
rewind()
Used to reset the file position indicator to the beginning of the file. It is similar to using fseek()
to move the file pointer to the beginning of the file, but rewind()
is a simpler function dedicated to zeroing the file pointer.
The function prototype is:
void rewind(FILE *stream);
parameter:
stream
: AFILE
pointer to an object representing the file for which you want to reset the file position indicator.
Return value: rewind()
The function has no return value (i.e. the return type is void
).
#include <stdio.h>
int main() {
FILE *pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen()");
return 1;
}
//读文件 - 文件中存放abcdef
int ch = fgetc(pf);
printf("%c\n", ch);// 输出:a 读完后文件指针往后+1 指向b
fseek(pf, 2, SEEK_CUR);//从当前文件指针往后偏移2个 (当前是b)
ch = fgetc(pf);
printf("%c\n", ch);// 输出:d
//记录当前偏移量
int pos = ftell(pf);//当文件指向d的时候 文件指针往后偏移1个 指向了e
printf("%d\n", pos);// 输出:4
//偏移量回到初始位置
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);// 输出:a
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
rewind()
The function is very convenient because there is no need to know the size of the file or use a specific offset, just call this function, the file pointer will return to the beginning of the file, and read or write data from the beginning.
6. Judgment of the end of file reading
6.1 misused 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.
Instead, it is applied when the file reading ends, judging whether the reading fails to end, or the end of the file is encountered.
1. Whether the reading of the text file is finished, judge whether the return value is EOF ( fgetc )
,或者 NULL ( fgets )
For example:
fgetc - If the read is normal, it will return the ascll code value of the character read, and if the read fails, it will return EOF
fgets - returns the address of the read data if the read was successful, or NULL if the read fails
fscanf - If the reading is normal, the number of data specified in the format string is returned; if the reading fails, the number of data that is less than the number of data specified in the format string is returned
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
Example of a text file:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int c;// 注意:int,非char,要求处理EOF
FILE *fp = fopen("test.txt", "r");
if (!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
// fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF)// 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
Example of a binary file:
#include <stdio.h>
enum {
SIZE = 5
};
int main() {
double a[SIZE] = {
1., 2., 3., 4., 5.};
FILE *fp = fopen("test.bin", "wb");// 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin", "rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp);// 读 double 的数组
if (ret_code == SIZE) {
puts("Array read successfully, contents: ");
for (int n = 0; n < SIZE; ++n)
printf("%f ", b[n]);
putchar('\n');
} else {
// error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
7. File buffer
The ANSIC standard uses the " buffer file system " to process data files. 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 . 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 data is read from the disk to the computer, the data read from the disk file is input to the memory buffer, and then the data is sent from the buffer to the program data area one by one. The size of the buffer is determined by the C compilation system.
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
int main() {
FILE *pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
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.