File operation - C language

 When we were studying before, we can find that when the program finishes running, the data we saved before will disappear , and we have to re-enter it when we run it again. In order to make the data we saved can be used next time, we will introduce it in this article Learn how to use file operations to save our data in files.

Table of contents

1. What is a file

2. File name

3. File pointer

4. File opening and closing

5. Sequential reading and writing of files

6. Random reading and writing of files

7. File Type 

8. Judgment of the end of the file

9. File buffer


1. What is a file

A file on a disk (hard disk) is a file

But in programming, we generally talk about two kinds of files: program files , data files

program files

Including the source program file (the suffix is ​​.c), the target file generated after compiling the source program (the suffix is ​​.obj in the Windows environment), and the executable program (the suffix is ​​.exe in the Windows environment).

 data file

The content of the file is not necessarily the program, but the data read and written when the program is running, such as the file from which the program needs to read data, or the file that outputs the content. For example the .txt file created.

This article discusses related operations on data files .

The input and output of data dealt with in previous chapters all target the terminal, that is, data is input from the keyboard of the terminal , and the running results are displayed on the screen of the terminal .


In fact, sometimes we will output the information to the disk , and then read the data from the disk into the memory for use when needed . Here, the files on the disk are processed.
 

2. File name

A file must have a unique file identifier for user identification 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 .

3. File pointer

When learning file operations, 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 stdio.h header file provided by the VS2008 compilation environment 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;

Here you only need to know that FILE is a structure, and it is enough to store the relevant information of the 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 .

Illustration:

4. File opening and closing

The file should be opened before reading and writing, and the file should be closed after use

When programming 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.

ANSIC stipulates that the fopen  function is used to open the file, and the fclose is used to close the file.

FILE* fopen(const char* filename, const char* mode);
返回文件信息区的地址       文件名          打开方式
int fclose (FILE* stream);
              要关闭的文件流  也就是文件信息区的地址

Open as follows

file usage meaning If the specified file does not exist
"r" (read-only) To enter data, open an existing file file go wrong
"w" (write only) To enter data, open a text file create a new file
"a" (append) Add data to the end of the text file go wrong
"rb" (read-only) To enter data, open a binary file go wrong
"wb" (write only) To enter data, open a binary file create a new file
"ab" (append) Add data to the end of a binary file go wrong
"r+" (read and write) Open a text file for reading and writing go wrong
"w+" (read and write) Create a new file for reading and writing create a new file
"a+" (read and write) Open a file for reading and writing at the end of the file create a new file
"rb+" (read and write) Open a binary file for reading and writing go wrong
"wb" (read and write) Create a new binary file for reading and writing create a new file
"ab+" (read and write) Open a binary file, read and write at the end of the file create a new file

Note: When a file is opened for writing (including "w"), the contents of the original file will be destroyed. 

 Sample code:

#include<stdio.h>

int main()
{
	//fopen返回文件信息区的地址,通过文件信息区操作文件
	//打开文件
	//打开成功,放回一个指向FILE类型的指针
	//打开失败,返回空指针
	FILE* pf = fopen("test.txt", "r");//这里文件是相对路径,也可以用绝对路径
	if (pf == NULL)
	{
		//打开失败
		perror("fopen:");//打印错误信息
		return 1;
	}

	//写文件
    //....

	//关闭文件
	fclose(pf);
	pf = NULL;//置空

	return 0;
}

5. Sequential reading and writing of files

Sequential reading and writing means that when we are reading or writing a file, the file pointer will automatically move backwards , so we can use a loop to read/write the entire file.

function:

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 document
binary output fwrite document

I/O/reading and writing are relative to memory 

Example:

fgetc and fputc   :

int fgetc ( FILE * stream );

int fputc ( int character, FILE * stream );

 Read/write the space of one character at a time, return the ASCII code value of the character if successful, and return EOF if failed

//写文件
#include<stdio.h>

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (fopen == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写文件
	//把26个字母写到文件中
	for(int i=0;i<26;i++)
	{
		fputc('a'+i, pf);//写一个字符
	}

	fclose(pf);
	pf = NULL;

	return 0;
}
//读文件
#include<stdio.h>

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (fopen == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件

	//读一个字符,返回躲到字符的ASCII码值
	//读取失败,或者读到文件末尾 返回EOF -1
	for(int i=0;i<26;i++)
	{
		int ch = fgetc(pf);
		//读取完一个会让文件内容指针向后移动,可以读取全部数据,不会只读a
		//与pf++不同,pf指向的是整个文件信息区
		printf("%c", ch);
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

fgets and fputs 

char * fgets ( char * str, int num, FILE * stream );

int fputs ( const char * str, FILE * stream );

 Read/write one line of space at a time, return the array address of saving a line of characters if successful, return NULL if failed

//fputs 写一行数据
#include<stdio.h>

int main()
{
	//打开文件
	FILE*pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写一行数据  hello world
	fputs("hello world\n",pf);
	fputs("xilanhua\n", pf);//会追加
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

//fgets 读一行数据
#include<stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//默认在程序所在工程目录下生成
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读一行数据 
	//char* fgets(char* str,int num, FILE* stream);
	//将读到的字符串拷贝放到 str字符数组中,
	//然后最多读num-1 个,因为第num个放 '\0'
	char arr[20];
	fgets(arr, 5, pf);//读一行,读到 '\n' 为止
	printf("%s\n", arr);//hell
	
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

formatted read and write

fprintf and fscanf :

int fprintf ( FILE * stream, const char * format, ... );
                                               ...可变参数列表
int fscanf ( FILE * stream, const char * format, ... );

It can be found that the parameter list part of fprintf and fscanf and printf and scanf just has more FILE* stream .

Example:

//fprintf 写文件
#include<stdio.h>

struct S
{
	int n;
	float f;
	char arr[20];
};
int main()
{
	struct S s = { 100,3.14f,"zhangsan" };

	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写文件
	fprintf(pf, "%d %f %s", s.n, s.f, s.arr);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}


//读文件
struct S
{
	int n;
	float f;
	char arr[20];
};

int main()
{
	struct S s;

	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	fscanf(pf, "%d %f %s", &(s.n), &(s.f), &(s.arr));

	printf("%d %f %s", s.n, s.f, s.arr);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

sprintf and sscanf

 Similar to fprintf and fscanf are sprintf and sscanf, the former is the operation between data and files, the latter is the operation between data and strings.

int sprintf ( char * str, const char * format, ... );

int sscanf ( const char * s, const char * format, ...);
  • sscanf : write a formatted data to a string
  • sprintf : read out the formatted data in the string

Example:

#include<stdio.h>
struct S
{
	int n;
	float f;
	char arr[20];
};
int main()
{
	struct S s = { 200,3.5f,"wangwu" };
	//把一个结构体转换为字符串
	char arr[50] = { 0 };
	sprintf(arr, "%d %f %s\n", s.n, s.f, s.arr);
	printf("字符串的数据:%s\n", arr);//以字符串的形式打印
	
	//把一个字符串 转换为 对应的格式化数据
	struct S tmp = { 0 };

	sscanf(arr, "%d %f %s", &tmp.n, &tmp.f, &tmp.arr);
	printf("格式化的数据:%d %f %s\n", tmp.n, tmp.f, tmp.arr);
	return 0;
}

The above function works for all input/output streams, so what does stream mean:

Flow can be understood as water flow, data transmission flows like water, and data can be put/taken in it.

Files, screens, networks, and other external devices, streams know how to interact with external devices, we only need to operate on streams.

 When reading and writing files: file stream

A C language program will open these 3 streams by default

  1. terminal device screen standard output stream stdout
  2. keyboard standard input stream stdin
  3. screen standard error stream stderr

stdout stdin stderr are all pointers to FILE*

So instead of opening and closing the keyboard, the file needs to be opened and closed.

 Use fputc to read data from the standard input stream

//标准输入流
#include<stdio.h>

int main()
{
	//从标准输入流读
	int ch = fgetc(stdin);
	printf("%c", ch);

	return 0;
}

//标准输出流
#include<stdio.h>

int main()
{
	fputc('a', stdout);//标准输出流,打印到屏幕上
	return 0;
}

 Two-level read and write files:

fwrite  and fread  :

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

Example:

//写文件
#include<stdio.h>

struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { "张三",20,95.5f };
	FILE* pf = fopen("test.dat", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fwrite(&s, sizeof(struct S), 1, pf);
	//字符串的二级制形式与文本的形式是相同的
	//整数和浮点型数据的二级制与文本形式是不同的

	//之前的函数使用字符的形式保存的,是认识的
	//现在是不认识的

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

//读文件
#include<stdio.h>

struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("test.dat", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fread(&s, sizeof(struct S), 1, pf);//返回值是实际成功读到的元素得个数
	
	printf("%s %d %f", s.name, s.age, s.score);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

6. Random reading and writing of files

We can achieve random reading of files by changing the pointing and offset of the file pointer . Mainly use the following three functions:

fseek:

  • Define the file pointer based on the position and offset of the file
int fseek ( FILE * stream, long int offset, int origin );

ftell:

  • Returns the offset of the file pointer relative to the starting position
long int ftell ( FILE * stream );

rewind:

  • Return the position of the file pointer to the beginning of the file
void rewind ( FILE * stream );

Example:

#include<stdio.h>

int main()
{
	//文件中已保存内容为abcdefg
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = 0;
	ch = fgetc(pf);//读取完,文件指针向后移
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	ch = fgetc(pf);
	printf("%c\n", ch);//c
	ch = fgetc(pf);
	printf("%c\n", ch);//d

	printf("%d\n", ftell(pf));//返回文件指针想对于文件起始位置的偏移量

	//我们使用fseek函数打印b
	fseek(pf, -3, SEEK_CUR);//文件指针当前位置
	ch = fgetc(pf);
	printf("%c\n", ch); //b
	
	fseek(pf, 1, SEEK_SET);//文件指针起始位置 
	ch = fgetc(pf);
	printf("%c\n", ch); //b

	fseek(pf, -6, SEEK_END);//文件指针末尾位置 
	ch = fgetc(pf);
	printf("%c\n", ch); //b

	rewind(pf);//让文件指针返回到起始位置
	printf("%d\n", ftell(pf));//0
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

7. File Type 

Depending on how the data is organized, data files are called text files or binary files .

Data is stored in binary form in memory, and 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 storage , it needs to be converted before storage. A file stored in the form of 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 it is output to the disk in the form of ASCII code, it will occupy 5 bytes (one byte for each character) on the disk, and if it is output in binary form, it will only occupy 4 words (int type) on the disk section (tested with VS2022).

storage method

1. Two-level system storage

Code example: 

#include<stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");//打印错误信息
		return 1;
	}
	//写文件
	int a = 10000;
	fwrite(&a, sizeof(int), 1, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

We opened the file and found the following:

 It is not in the form of binary system, because we open the file in text mode, we can change its opening mode by adding the existing item test.txt:

we can see 

 This is little-endian storage, so you can see this effect.

2. Use fprintf to store data (ASCII code value form)

#include<stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");//打印错误信息
		return 1;
	}
	//写文件
	int a = 10000;
	fprintf(pf,"%d",a);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

result 

 What is stored here is the ASCII code value (hexadecimal) corresponding to the characters '1' '0' '0' '0' '0'.

 So we can see 10000 when we open it in text form.

8. Judgment of the end of the file

The wrongly used feof
keeps 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) or NULL (fgets)

For example:

  • getc judges whether it is EOF.
  • 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.

The conditions for these judgments are all related to the return values ​​of these functions. 

Another function of the read function:

When the read function fails to read, return, and set an error status at the same time, use ferror to detect this status,

Encounter the end of the file, return, and set a state at the same time, use feof to detect this state 

feof and ferror

  • feof returns a non-zero value if it is a read operation that ends when encountering the end of file flag
  • ferror Returns a non-zero value if it is a read operation that ends when it encounters a read failure (error)

Example:

#include<stdio.h>

int main()
{
	//文件内部abcdef
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ",ch);
	}

	if (feof(pf))//检查是否有这样的状态设置,有返回非0
	{
		printf("遇到文件结束标志而结束\n");
	}
	else if(ferror(pf))
	{
		printf("遇到文件错误为结束");
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

9. 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 opens 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 (full buffer), and then the data is sent from the buffer to the program data area (program variables, etc.) one by one. The size of the buffer is determined by the C compilation system. This is done to give the operating system more time to process other stuff .

 This is still relatively abstract, because we can't see the existence of the buffer.

We can feel the existence of the buffer through the following piece of code:

#include<stdio.h>
#include<windows.h>

int main()
{
	FILE* pf = fopen("test.txt", "w");
	fputs("abcdef", pf);//先将数据放在缓冲区
	printf("睡眠10秒-已经写了数据,打开test.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区,才将输入缓冲区的数据写到文件(磁盘)

	printf("在睡眠10秒—此时,再打开test.txt文件,文件有内容了");
	Sleep(10000);
	fclose(pf);//fcolse 在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

fflush will actively refresh the buffer , so that the data in the buffer is written to the disk. After refreshing, you can find that the data is in the file, and the file was not there in the previous 10 seconds.

Before we finished running the program, we will find that the data has entered the file, because fclose will also refresh the buffer

end of article

Guess you like

Origin blog.csdn.net/qq_72916130/article/details/131464878