<C Language> File Operation

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.hhas 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.

fopenFunction: used to open the file.

Function prototype:

FILE *fopen(const char *filename, const char *mode);
  • filenameThe argument is the name of the file to open, which can be a relative path or an absolute path.
  • modeThe 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.

fcloseFunction: used to close the file.

Function prototype:

int fclose(FILE *stream);
  • streamThe argument is a pointer fopento the type returned by the function FILEthat 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盘没有该目录

insert image description here

3. Sequential reading and writing of files

3.1 fgetc and fputc

fgetcand fputcstandard 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.

fgetcfunction:

int fgetc(FILE *stream);

fgetcFunction to read a character from the given file stream (represented by FILEa pointer ). streamIt 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;
}

insert image description here

fputcfunction:

int fputc(int character, FILE *stream);

fputcFunction to write a character to the specified file stream (represented by FILEa 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;
}

insert image description here

fgetcand fputcare lower-level file I/O functions. C language also provides more advanced file read and write functions, such as fgetsand fputs, which are more convenient for reading and writing strings and a line of text.

3.2 fgets and fputs

fgetsand are standard library functions for reading and writing files, which are more convenient for reading and writing strings and a line of text fputsthan and fgetc.fputc

fgetsfunction:

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

fgetsFunction to read a line of text from the given file stream (represented by FILEa pointer stream) and store it in a character array str. The parameter nspecifies 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 \0terminated 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;
}

insert image description here

fputsfunction:

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

fputsFunction to write a null- \0terminated string strto the specified file stream (represented by FILEa 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;
}

insert image description here

As with the previous fgetcand fputc, fgetsthe and fputsshould 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

fscanfand fprintfare standard library functions for formatted file input and output. They are similar to scanffunctions printf, but can read data from and write data to files.

fprintffunction:

int fprintf(FILE *stream, const char *format, ...);

fprintfThe function is used to write the formatted data formatto the given file stream (represented by FILEthe pointer stream) according to the specified format string. formatThe argument is a format string, similar to printfformat strings in functions. According to the format specifiers in the format string, fprintfthe 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;
}

insert image description here

fscanffunction:

int fscanf(FILE *stream, const char *format, ...);

fscanfThe function is used to read data according to the specified format string from the given file stream (represented by FILEpointer ). 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.streamformatformatscanf

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;
}

insert image description here

Like the previous functions, fscanfand fprintfshould 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

freadand fwriteare standard library functions for binary file reading and writing. Unlike the previous functions such as fgets, fputs, fscanfand 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.

fwritefunction:

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

fwriteThe function ptrwrites data from the memory region pointed to by the given file stream (represented by FILEthe pointer stream). The parameter sizerepresents the number of bytes per data item, while nmembthe number of data items to write. The function returns the actual number of data items written successfully, which should usually be nmembcompared 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;
}

insert image description here

freadfunction:

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

freadThe function reads data from the given file stream (represented by FILEa pointer stream) and stores it in ptrthe memory area pointed to by . The parameter sizerepresents the number of bytes per data item, while nmembthe number of data items to read. The function returns the actual number of data items successfully read, which should usually be nmembcompared 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;
}

insert image description here

freadand fwriteare 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 - screen

Their 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;
}

insert image description here

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:

  • streamFILE: 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 of long inttype.
  • 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: A FILEpointer to the object, indicating that you want to get the file at the current location.

return value:

  • On success, ftell()returns a long intvalue 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: A FILEpointer 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.
insert image description here

#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.

Guess you like

Origin blog.csdn.net/ikun66666/article/details/131993458