C++学习: 文件I/O

作者: 苏丙榅
原文链接: https://subingwen.cn/c/file/

文章目录

    • 1. 文件概述
      • 1.1 什么是文件I/O
      • 1.2 磁盘文件分类
    • 2. 文件的打开和关闭
      • 2.1 文件指针
      • 2.2 打开文件
    • 2.3 关闭文件
    • 3. 文件的读写
      • 3.1 按照字符读写文件
        • 3.1.1 写文件
        • 3.1.2 读文件
        • 3.1.3 EOF
      • 3.2 按照行读写文件
        • 3.2.1 写文件
        • 3.2.2 读文件
      • 3.3 按照块读写文件
        • 3.3.1 写文件
        • 3.3.2 读文件
      • 3.4 文件指针偏移
        • 3.4.1 fseek
        • 3.4.2 ftell
    • 4. 删除、重命名文件
      • 4.1 删除文件
      • 4.2 重命名文件
    • 5. 文件缓冲区
    • 6. 附录

1. 文件概述

1.1 什么是文件I/O

文件 I/O(Input/Output)指的是程序与外部文件之间的数据传输操作。在许多编程语言中,包括C、C++、Python等,都提供了用于进行文件 I/O 操作的内置函数或库。

文件 I/O 操作主要涉及两个方面:读取文件和写入文件

读取文件:

  • 1.打开文件:使用文件打开函数(如fopen)打开待读取的文件,指定文件名和打开模式(如只读、读写等)。
  • 2.读取文件内容:使用文件读取函数(如fread、fgets等)从打开的文件中读取内容,将内容保存到程序中的变量中进行处理。
  • 3.关闭文件:使用文件关闭函数(如fclose)关闭已打开的文件,释放系统资源。

写入文件:

  • 1.打开文件:使用文件打开函数(如fopen)打开待写入的文件,指定文件名和打开模式(如只写、追加等)。
  • 2.写入文件内容:使用文件写入函数(如fwrite、fprintf等)将程序中的数据写入到打开的文件中。
  • 3.关闭文件:使用文件关闭函数(如fclose)关闭已打开的文件,释放系统资源。

在计算机中,我们最常见的就是磁盘文件,磁盘文件由文件系统管理,文件系统负责在磁盘上分配、组织和管理文件的存储空间。每个磁盘文件都有一个唯一的文件名,可以通过文件名来定位和访问文件。只有在使用的时候磁盘数据才会被加载到内存中

1.2 磁盘文件分类

计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储
在这里插入图片描述
从用户或者操作系统使用的角度(逻辑上)把文件分为两大类:

  • 文本文件:基于字符编码的文件

    • 可读性高:文本文件的内容是针对人类可读的,可以使用文本编辑器或终端窗口进行查看和编辑。
    • 无格式化信息:文本文件不包含任何格式化信息,如字体、颜色、大小或页面布局等
    • 逐行存储:文本文件通常以逐行的方式存储文本内容。每行以换行符(如回车符或换行符)结尾,用于划分不同的文本行。
    • 压缩效率较低:文本文件通常包含了大量重复的文本数据,而通用的文本压缩算法效果有限。
    • 通用性好:文本文件使用的是常见的字符编码(ASCII、UTF-8、UTF-16 等),在各种平台和操作系统上被广泛支持和使用。
  • 二进制文件:基于值编码的文件

    • 机器可读性:二进制文件不是以可读的文本形式存储的,其中的数据以计算机能够理解和处理的方式进行编码。
    • 可包含复杂的结构:这些结构(如数组、记录、树、图等)在文件中具有特定的布局和编码方式,以便计算机能够正确读取和解析它们。
    • 任意数据类型:二进制文件可以包含任意的数据类型,包括整数、浮点数、布尔值、字符、字节、指针等。它们会编码为特定的字节序列
    • 高效的存储空间利用:二进制文件可以使用各种压缩算法、编码方式和数据结构,以减小文件大小并提高数据的存储效率。
    • 平台和操作系统相关性:二进制文件的数据编码方式和存储结构可能与特定的机器、操作系统或平台相关,所以在跨平台或不同操作系统之间处理二进制文件时需要适应相应的技术和工具。

文本文件在许多领域中都有着广泛的应用,如日志文件、配置文件、源代码文件、文档、电子邮件等。它是人与计算机之间常用的信息交流和数据存储方式之一。

二进制文件在许多应用中扮演着重要的角色,如可执行文件、图像文件、音频文件、视频文件、数据库文件等。它们提供了一种灵活、高效的方式来存储和处理大量的非文本数据,为各种应用程序和领域提供了丰富的功能和性能。

2. 文件的打开和关闭

2.1 文件指针

在C语言中,文件指针是用来处理文件读写操作的一种特殊类型的指针。C语言提供了一组文件操作函数,通过这些函数可以创建、打开、关闭、读取和写入文件,文件指针在这些操作中起到了关键的作用。

在使用文件指针之前,需要先定义一个指向FILE类型的指针变量,用于表示文件指针。FILE类型定义在stdio.h头文件中。

typedef struct
{short           level;  // 缓冲区"满"或者"空"的程度 unsigned        flags;  // 文件状态标志 char            fd;     // 文件描述符unsigned char   hold;   // 如无缓冲区不读取字符short           bsize;  // 缓冲区的大小unsigned char* buffer;  // 数据缓冲区的位置 unsigned        ar;     // 指针,当前的指向 unsigned        istemp; // 临时文件,指示器short           token;  // 用于有效性的检查 
}FILE;

FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。

在进行文件操作的时候,定义文件指针的方式如下:

FILE *filePointer;

C语言中对文件的各种操作都是基于文件指针来实现的,每操作一个文件都应该让其对应一个文件指针:
在这里插入图片描述
在C语言中,有三个默认的文件指针,它们对应标准的输入/输出流,可以用于读取用户输入和向屏幕输出信息,或者用于错误信息的输出。

  • 标准输入(stdin):标准输入是默认的输入文件指针。它通常与键盘相连,用于接收用户的输入。可以使用输入函数如scanf来读取标准输入的内容。

  • 标准输出(stdout):标准输出是默认的输出文件指针。它通常与屏幕相连,用于向屏幕输出信息。可以使用输出函数如printf来向标准输出打印内容。

  • 标准错误(stderr):标准错误是默认的错误输出文件指针。它也通常与屏幕相连,用于输出错误信息。可以使用输出函数如fprintf来向标准错误打印错误信息。

这些文件指针是预定义的,可以在程序中直接使用,无需手动打开或关闭。它们的文件指针类型是FILE*。

例如,可以使用scanf函数从标准输入读取用户的输入

int num;
// 从标准输入读取一个整数
scanf("%d", &num); 

也可以使用printf函数向标准输出打印信息:

int num = 10;
// 向标准输出打印信息
printf("The number is: %d\n", num); 

在C语言中,默认的文件指针提供了简单而便捷的输入和输出功能,使得对用户输入的处理和对程序输出的控制更加灵活和方便。

2.2 打开文件

除了标准输入(stdin)、标准输出(stdout)和标准错误(stderr)三个文件指针可以直接使用以外,其它的文件指针在使用之前必须进行初始化,初始化方式就是将相应的文件打开,这需要用到C语言提供的fopen函数。

fopen函数的原型如下

#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);
  • 参数:
    • filename:指定要打开的文件名,需要加上路径(相对、绝对路径)
    • mode:指定文件的打开模式
  • 返回值:
    • 成功:返回指向打开文件的文件指针
    • 失败:返回 NULL

关于fopen函数第二个参数mode对应的文件打开模式如下表:

在这里插入图片描述

  • b是二进制模式的意思
    • b只是在Windows有效
    • Linuxr和rb的效果是一样的
  • Unix/Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
  • 在Windows平台下,以“文本”方式打开文件,不加b:
    • 当读取文件的时候,系统会将所有的"\r\n"转换成 "\n"
    • 当写入文件的时候,系统会将 "\n" 转换成"\r\n"写入
    • "二进制"方式打开文件,则读\写都不会进行这样的转换
  • 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,“\r\n” 作为两个字符原样输入输出

使用fopen函数打开一个文件,可以使用相对路径,也可以使用绝对路径:

// ========== 相对路径 ========== 
// 打开当前目录 hello.txt 文件
FILE* fp = fopen("hello.txt", "r");
fp = fopen("./hello.txt", "r");// 打开当前目录(test)下 hello.txt 文件
fp = fopen("./test/hello.txt", "r");// 打开当前目录上一级目录(相对当前目录)hello.txt 文件
fp = fopen("../hello.txt", "r");//  ========== 绝对路径 ========== 
// 打开D盘 test 目录下一个叫 hello.txt 文件
fp = fopen("d:\\test\\hello.txt", "r");
fp = fopen("d:/test/hello.txt", "r");

在Windows中路径的根节点的对应的盘符,描述路径使用的分隔符是反斜杠\,但是在C语言中反斜杠是转义字符,所以需要将其转换为普通字符,正确的写法是写两个反斜杠\\,除此之外还可以使用斜杠/来描述路径。

在Linux中路径的根节点的/,描述路径使用的分隔符是/。
下面是一个示例程序,它演示了如果打开文件和判断文件是否打开成功了:

#include <stdio.h>int main() 
{FILE* fp = fopen("hello.txt", "r");if (fp == NULL){perror("fopen");}else{printf("文件打开成功!\n");}return 0;
}

perror是一个C标准库函数,它可以将最后一次发生的错误信息输出到终端。它的函数原型如下:

#include <stdio.h>
void perror(const char *str);

perror函数接受一个字符串参数 str,用于作为错误信息的前缀。它会自动获取最近一次错误的错误码,并将相应的错误描述信息格式化输出到标准错误流(stderr)中。

2.3 关闭文件

通过open函数可以打开一个文件,并基于得到的文件指针对文件进行各种操作,使用完毕后还需要将文件关闭,其原因如下:

  • 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
  • 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败

C语言中提供的文件关闭函数是fclose,函数原型如下:

#include <stdio.h>
int fclose(FILE * stream);
  • 参数:接受一个文件指针 stream,用于指定要关闭的文件。它会将缓冲区中的数据写回到文件中,并释放与文件相关的资源。
  • 返回值:返回一个整数值来指示关闭操作的成功与否。
    • 成功:关闭文件,它会返回0;
    • 失败:返回非零值
#include <stdio.h>int main() 
{FILE* fp = fopen("hello1.txt", "r");if (fp == NULL){perror("fopen");return -1;}else{printf("文件打开成功!\n");flcose(fp);}return 0;
}

如果没有在程序中明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭,但是还是建议大家养成用完之后随时关闭文件的好习惯

3. 文件的读写

3.1 按照字符读写文件

3.1.1 写文件

fputc是一个C标准库函数,用于将一个字符写入到文件中。它的函数原型如下:

#include <stdio.h>
int fputc(int character, FILE *stream);
int putc(int character, FILE *stream);
  • 参数:
    • character:要写入的字符,注意这个参数是整形
    • stream:文件指针,对应要写入字符的文件
  • 返回值:
    • 成功:返回写入的字符
    • 失败:返回 EOF

在标准C库中,putc函数实际上是一个宏,而不是一个真正的函数。可以将其视为fputc函数的别名。因此,它们的功能是相同的。

以下是一个示例,展示了如何使用fputc函数将字符写入文件:

#include <stdio.h>
#include <string.h>int main()
{char* buf = "One world one dream!";FILE* fp = fopen("example.txt", "w");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}int len = strlen(buf);for (int i = 0; i < len; ++i){int ch = fputc(buf[i], fp);printf("%c", ch);}printf("\n");fclose(fp);return 0;
}

在上面的示例中,程序打开名为example.txt的文件以供写入,使用fputc函数将字符串"One world one dream!"写入文件,并根据返回值将写入文件的字符打印到终端。

需要注意的是,fputc函数每次只能写入一个字符。如果需要连续写入多个字符,可以像上面程序中一样使用循环的方式多次调用fputc函数。

fputc函数通常用于以字符形式写入文件,特别适用于处理文本文件。如果需要以二进制形式写入数据,可以使用fwrite函数。

3.1.2 读文件

fgetc是一个C标准库函数,用于从文件中读取一个字符。它的函数原型如下:

#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);

fgetc函数接受一个文件指针 stream,用于指定要从中读取字符的文件。它会从文件中读取一个字符,并返回读取的字符(或者在到达文件结尾或发生错误时返回EOF)。

在标准C库中,getc函数实际上是一个宏,而不是一个真正的函数。可以将其视为fgetc函数的别名。因此,它们的功能是相同的。

以下是一个示例,展示了如何使用fgetc函数从文件中读取字符:

#include <stdio.h>int main() 
{FILE* fp = fopen("example.txt", "r");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}char ch;while ((ch = fgetc(fp)) != EOF){printf("%c", ch);}printf("\n");fclose(fp);return 0;
}

fgetc函数每次只能读取一个字符。在上面的示例中,程序以只读的方式打开名为example.txt的文件,并通过循环使用fgetc函数读取了文件中的所有字符。

fgetc函数通常用于以字符形式读取文件,特别适用于处理文本文件。如果需要以二进制形式读取数据,可以使用fread函数。

3.1.3 EOF

在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志

#define EOF  (-1)

当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用于检查文件流的文件结束标志。feof函数既可用以判断二进制文件又可用以判断文本文件。

feof的函数原型如下:

#include <stdio.h>
int feof(FILE *stream);

feof函数接受一个文件指针 stream,用于指定要检查的文件流。它会检查文件流的文件结束标志:

  • 返回一个非零值(真):表示已经到文件结尾
  • 返回0(假):表示没有到文件结尾

以下是一个示例,展示了如何使用feof函数检查文件的结束标志:

#include <stdio.h>int main() 
{FILE* fp = fopen("example.txt", "r");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}char ch;while ((ch = fgetc(fp))){if (feof(fp)) {printf("\nReached end of file.\n");break;}printf("%c", ch);}printf("\n");fclose(fp);return 0;
}

程序打开名为example.txt的文件以供读取,并使用feof函数在每次读取字符前检查文件结束标志。

需要注意的是,在读取文件时,feof函数需要在读取操作之后进行调用,以保证能够正确判断文件结束标志。

3.2 按照行读写文件

3.2.1 写文件

fputs是一个C标准库函数,用于将字符串写入文件。它的函数原型如下:

#include <stdio.h>
int fputs(const char *string, FILE *stream);
  • 参数:

    • string:字符串的指针,表示要写入的内容
    • stream:文件指针,用于指定要写入字符的文件
  • 返回值:

    • 成功:返回一个非负值
    • 失败:返回EOF

以下是一个示例,展示了如何使用fputs函数将字符串写入文件:

#include <stdio.h>int main() 
{const char* string = "Hello, world!";FILE* fp = fopen("example.txt", "w");if (fp == NULL) {printf("Failed to open the file.\n");return -1;}if (fputs(string, fp) == EOF) {printf("Failed to write the string.\n");return -1;}fclose(fp);return 0;
}

在上面的示例中,程序打开名为example.txt的文件以供写入,并使用fputs函数将字符串"Hello, world!"写入文件中。

需要注意的是,fputs函数只能写入字符串,而不能写入其他类型的数据。如果需要将其他类型的数据写入文件,可以使用例如fprintf函数等。

此外,fputs函数每次只能写入一个字符串,如果需要连续写入多个字符串,可以多次调用fputs函数。如果需要在每个字符串之间添加换行符或其他分隔符,需要在调用fputs之后手动添加。

3.2.2 读文件

fgets是一个C标准库函数,用于从文件中读取一行字符。它的函数原型如下:

#include <stdio.h>
char *fgets(char *string, int size, FILE *stream);
  • 参数:

    • string:字符指针,用于存储读取的字符
    • size:指定要读取的最大字符数(包括终止符)
    • stream:文件指针,用于指定要从中读取字符的文件
  • 返回值:

    • 成功:返回参数string的首地址
    • 失败:返回NULL

以下是一个示例,展示了如何使用fgets函数从文件中读取一行字符:

#include <stdio.h>int main()
{char line[100]; // 假设一行最多100个字符FILE* fp = fopen("example.txt", "r");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}while (fgets(line, sizeof(line), fp) != NULL) {printf("Read line: %s", line);}fclose(fp);return 0;
}

在上面的示例中,程序打开名为example.txt的文件以供读取,并使用fgets函数从文件中逐行读取字符。每次成功读取一行后,程序将该行输出到控制台。如果到达文件尾或发生错误,则退出循环。

需要注意的是,fgets 读取的行包括换行符,因此在输出时,可以选择输出整行(包括换行符)或者忽略换行符。

fgets函数通常用于以文本行的形式读取文件内容,特别适用于处理文本文件。如果需要以二进制方式从文件中读取数据,可以使用fread函数。

3.3 按照块读写文件

3.3.1 写文件

fwrite是一个C标准库函数,用于以二进制形式将数据写入文件。它的函数原型如下:

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
  • 参数:

    • ptr:指向要写入数据的指针
    • size:要写入的每个元素的字节数
    • count:要写入的元素数量
    • stream:文件指针,用于指定要写入数据的文件
  • 返回值:

    • 成功:返回写入的元素数量,即count的值
    • 失败:或者返回一个小于count的值

fwrite函数会将指针ptr指向的数据写入到文件中。写入的总字节数是sizecount相乘的积。

以下是一个示例,展示了如何使用fwrite函数将数据以二进制形式写入文件:

#include <stdio.h>
#include <string.h>struct Person 
{char name[20];int age;double height;
};int main() 
{struct Person person;FILE* fp = fopen("example.bin", "wb");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}strncpy(person.name, "John Doe", sizeof(person.name));person.age = 30;person.height = 1.8;if (fwrite(&person, sizeof(person), 1, fp) != 1) {printf("Failed to write the data.\n");return 1;}fclose(fp);return 0;
}

在上面的示例中,程序创建了一个名为example.bin的二进制文件,用于写入Person结构体的数据。程序使用fwrite函数将person结构体的数据写入文件中。

需要注意的是,示例程序中fwrite函数是以二进制形式写入数据,因此在读取数据时,也需要以二进制方式读取,使用fread函数来读取相应的数据。

3.3.2 读文件

fread是一个C标准库函数,用于从文件中以二进制形式读取数据。它的函数原型如下:

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
  • 参数:

    • ptr:指向用于存储读取数据的缓冲区的指针
    • size:每个元素的字节数
    • count:要读取的元素数量
    • stream:文件指针,用于指定要从中读取数据的文件
  • 返回值:

    • 成功:返回实际读取的元素数量
    • 失败:返回的元素数量与count不相等

fread函数会从文件中读取指定数量的元素,每个元素占据size个字节,将它们存储在ptr指向的缓冲区中。

以下是一个示例,展示了如何使用fread函数从文件中以二进制形式读取数据:

#include <stdio.h>struct Person 
{char name[20];int age;double height;
};int main() 
{struct Person person;FILE* fp = fopen("example.bin", "rb");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}if (fread(&person, sizeof(person), 1, fp) != 1) {printf("Failed to read the data.\n");return 1;}printf("Name: %s\n", person.name);printf("Age: %d\n", person.age);printf("Height: %f\n", person.height);fclose(fp);return 0;
}

在上面的示例中,程序以二进制形式打开名为example.bin的文件,并使用fread函数读取文件中的数据到person结构体中。

需要注意的是,fread函数是以二进制形式读取数据,因此在写入数据时,也需要以二进制方式写入,使用fwrite函数来写入相应的数据。

3.4 文件指针偏移

3.4.1 fseek

fseek是一个C标准库函数,用于在文件中定位文件指针的位置。它的函数原型如下:

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
  • 参数:
    • stream:文件指针,用于指定要定位的文件
    • offset:指定文件指针的偏移量
    • whence:指定文件指针的起始位置,它对应的值有三个
    • SEEK_SET:表示从文件开始处偏移,偏移量为offset个字节
    • SEEK_CUR:表示从当前位置偏移,偏移量为offset个字节
    • SEEK_END:表示从文件末尾处偏移,偏移量为offset个字节
  • 返回值:
    • 成功:返回 0
    • 失败:返回非零值

以下是一个示例,展示了如何使用fseek函数来定位文件指针的位置:

#include <stdio.h>int main() 
{FILE* fp = fopen("example.txt", "rb");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}char buf[1024] = { 0 };fseek(fp, 10, SEEK_SET); fread(buf, 1, sizeof(buf), fp);printf("从头部偏移10字节开始读数据, 内容是: %s\n", buf);// rewind(fp);fseek(fp, 0, SEEK_SET);  fread(buf, 1, sizeof(buf), fp);printf("从头部开始读数据, 内容是: %s\n", buf);fclose(fp);return 0;
}

在上面的示例中,程序打开了名为example.txt的文件以供读取,并使用fseek函数将文件指针从开始处偏移10个字节,然后再把文件中的数据读出来,此时文件指针已经移动到了最后,如果要从头读文件需要再次把文件移动到文件头。

需要注意的是,fseek函数在二进制文件和文本文件中的行为可能有所不同。

  • 文本文件中,由于使用的编码不同,字符和行的长度可以可变,因此将文件指针定位在某个位置可能无法准确找到这个位置。
  • 二进制文件中,fseek函数可以在任意位置准确定位。

如果想要将文件指针移动到文件的头部,有两种写法:

fseek(fp, 0, SEEK_END);   // 文件指针移动到文件尾部
fseek(fp, 0, SEEK_SET);   // 文件指针移动到文件头部
rewind(fp);               // 文件指针移动到文件头部

rewind是一个C标准库函数,它用于将文件指针重新定位到文件的起始位置。它的函数原型如下:

#include <stdio.h>
void rewind(FILE *stream);

rewind函数接受一个参数:stream是一个文件指针,用于指定要重新定位的文件。

另外,ftell函数可用于获取当前文件指针的偏移量。

3.4.2 ftell

ftell是一个C标准库函数,用于获取文件指针的当前位置(偏移量)。它的函数原型如下:

#include <stdio.h>
long ftell(FILE *stream);
  • 参数:

    • stream:文件指针,用于指定要获取当前位置的文件。
  • 返回值:

    • 成功:表示文件指针相对于文件起始位置的偏移量。
    • 失败:返回值为负数

以下是一个示例,展示了如何使用ftell函数获取文件指针的当前位置:

#include <stdio.h>int main() 
{long position;FILE* fp = fopen("example.txt", "r");if (fp == NULL) {printf("Failed to open the file.\n");return 1;}position = ftell(fp);if (position < 0) {printf("Failed to get the current position.\n");return 1;}printf("Current position: %ld\n", position);fseek(fp, 15, SEEK_SET);position = ftell(fp);printf("Current position: %ld\n", position);fclose(fp);return 0;
}

在上面的示例中,程序打开了名为example.txt的文件以供读取,并使用ftell函数获取文件指针的当前位置。

需要注意的是,ftell函数返回的偏移量是以字节为单位的相对值。初始位置为0,向文件末尾方向的偏移量为正,向文件开始方向的偏移量为负。

此外,在Linux系统中,还可以使用ftello函数或ftello64函数来处理大文件(超过2GB)的偏移量。这些函数返回的偏移量类型为off_t`,可以处理更大的文件。

4. 删除、重命名文件

4.1 删除文件

remove是一个C标准库函数,用于删除文件。它的函数原型如下:

int remove(const char *filename);

remove函数接受一个字符串参数 filename,用于指定要删除的文件的路径和名称。它会删除指定路径下的文件,如果成功删除则返回0,否则返回非零值。

以下是一个示例,展示了如何使用remove函数删除文件

#include <stdio.h>int main() 
{if (remove("example.txt") != 0) {printf("Failed to delete the file.\n");return 1;}printf("File deleted successfully.\n");return 0;
}

在上面的示例中,程序调用remove函数尝试删除名为example.txt的文件。如果成功删除文件,则输出"File deleted successfully.",否则,输出"Failed to delete the file."

请注意,使用remove函数删除文件时需谨慎,因为该操作是不可撤销的。可以在调用remove函数之前,确保文件不再需要,并且已经完成对文件的所有操作。另外,需要确保拥有足够的权限来删除文件,否则删除操作可能会失败

4.2 重命名文件

rename是一个C标准库函数,用于重命名文件或将文件移动到另一个位置。它的函数原型如下:

int rename(const char *old_filename, const char *new_filename);

rename函数接受两个字符串参数 old_filenamenew_filename,分别表示原始文件名和新文件名(包括路径)。它会将原始文件重命名为新文件名或将文件移动到新位置。如果成功重命名或移动文件,则返回0;否则,返回非零值。

以下是一个示例,展示了如何使用rename函数重命名文件:

#include <stdio.h>int main() 
{if (rename("oldname.txt", "newname.txt") != 0) {printf("Failed to rename the file.\n");return 1;}printf("File renamed successfully.\n");return 0;
}

在上面的示例中,程序调用rename函数将名为oldname.txt的文件重命名为newname.txt。如果成功重命名文件,则输出"File renamed successfully.“,否则输出"Failed to rename the file.”。

请注意,使用rename函数重命名文件时需谨慎,因为该操作是不可撤销的。同时,还需要确保有足够的权限来执行重命名操作,并且新文件名不会与现有文件冲突。

此外,rename函数还可以用于将文件移动到不同的目录中,只需要在新文件名中指定目标文件夹的路径。例如:

rename("file.txt", "new/location/newfile.txt");

这样,就可以将文件file.txt移动到new/location目录下,并命名为newfile.txt。但是一定要注意,该函数并不会创建新的目录,也就是说要保证new/location/目录是存在的,移动才能成功。

5. 文件缓冲区

ANSI C标准采用“缓冲文件系统”处理数据文件。缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。

如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)。
在这里插入图片描述
我们编写的C程序和磁盘文件之间的关系是这样的:

  • 磁盘文件,一般保存在硬盘、U盘等掉电不丢失的磁盘设备中,在程序需要访问的时候加载到内存

  • 通过程序对内存中的数据进行编辑处理后,再将其保存到磁盘文件中

  • 程序与磁盘文件之间交互,可以不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率

    • 磁盘(机械硬盘)读写速度相较于内存不是一个数量级,频繁读写磁盘效率是比较低的
    • 给文件函数提供缓冲区以提高效率,这相当于是空间换时间

fflush是一个C标准库函数,用于将输出缓冲区的内容立即写入文件
它的函数原型如下:

#include <stdio.h>
int fflush(FILE *stream);

fflush函数接受一个文件指针stream作为参数,用于指定要刷新缓冲区的文件。它会将输出缓冲区中的内容强制写入文件,并清空缓冲区。

fflush函数返回一个整数值来指示刷新操作的成功与否。如果成功刷新缓冲区,它会返回0;否则,返回非零值。通常情况下,我们可以将其与0进行比较,以确定刷新是否成功。

以下是一个示例,展示了如何使用fflush函数将输出缓冲区的内容立即写入文件:

#include <stdio.h>int main() 
{FILE* fp = fopen("example.txt", "w");if (fp == NULL){perror("fopen");return 1;}fprintf(fp, "Hello, World!");if (fflush(fp) != 0){printf("Failed to flush the output buffer.\n");return 1;}fclose(fp);return 0;
}

在上面的示例中,程序打开名为example.txt的文件进行写入操作,并使用fprintf函数向文件写入字符串。然后,使用fflush函数刷新输出缓冲区,将字符串立即写入文件中。

使用fflush函数可以确保数据被及时写入文件,而不是在程序结束时或者缓冲区达到一定大小时才写入。这在某些情况下很有用,例如当需要及时查看或共享文件内容时。

fprintf是一个C标准库函数,用于将格式化的数据写入文件中。

它的函数原型如下:

#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);

fprintf函数接受一个文件指针 stream、一个格式化字符串 format 和一系列的可变参数,用于按照指定的格式将数据写入到指定的文件中。

fprintf函数与printf函数的用法类似,不同之处在于它将结果输出到指定的文件,而不是标准输出流(stdout)

6. 附录

字符编码是一种将字符映射到数字表示的方法。由于计算机只能处理数字,因此需要一种方式将字符转换为可以在计算机中存储和处理的数据。

常见的字符编码方式有以下几种:

  • ASCII(American Standard Code for Information Interchange):ASCII是最早和最简单的字符编码方案,使用7位二进制数表示128个常用的字符,包括英文字母、数字、标点符号和一些控制字符。ASCII编码在计算机系统中得到广泛应用。

  • Unicode:Unicode是一种更全面的字符编码方案,用于表示世界上几乎所有的字符集。Unicode编码使用固定的代码点表示字符,可以包含范围广泛的字符集,包括所有国际语言的字符、符号和标点符号。Unicode使用不同的编码方案,最为广泛使用的是UTF-8、UTF-16和UTF-32。

    • UTF-8(Unicode Transformation Format-8):UTF-8是一种可变长度字符编码方案,可以表示Unicode字符集中的所有字符。UTF-8使用1到4个字节表示不同的字符,对于ASCII字符,使用1个字节表示,而对于非ASCII字符,使用多个字节表示。UTF-8是互联网上最常用的字符编码方式之一。

    • UTF-16(Unicode Transformation Format-16):UTF-16是一种固定长度字符编码方案,使用16位或32位的代码单元表示不同的字符。UTF-16可以表示基本多语言平面(BMP)中的所有字符,对于非BMP字符,使用一对代理对来表示。UTF-16在一些基于Windows的系统中广泛使用。

    • UTF-32(Unicode Transformation Format-32)是一种将Unicode字符编码为32位无符号整数的字符编码方案。它使用固定长度的四个字节来表示每个Unicode字符,因此每个字符都被编码为一个32位整数。在实践中,UTF-32一般用于要求高效随机访问和字符处理的应用,例如一些特定的系统级库或字符处理算法。对于一般的文本存储和传输,通常推荐使用UTF-8或UTF-16编码,以平衡存储空间和处理效率的需求。

字符编码在计算机领域中非常重要,它决定了计算机如何处理和表示文本数据。选择适当的字符编码方式可以确保文本正确地显示和处理,避免乱码和字符转换错误的问题。在实际应用中,需要根据具体的需求和环境选择合适的字符编码方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/107850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[架构之路-237]:目标系统 - 纵向分层 - 网络通信 - DNS的递归查询和迭代查询

目录 一、DNS协议与DNS系统架构 1.1 什么是DNS协议 1.2 为什么需要DNS协议 1.3 DNS系统架构 二、DNS系统的查询方式 2.1 递归与迭代的比较 2.2 DNS递归查询 2.3 DNS迭代查询 一、DNS协议与DNS系统架构 1.1 什么是DNS协议 DNS&#xff08;Domain Name System&#xff…

2023年中国商业版服务器操作系统市场发展规模分析:未来将保持稳定增长[图]

服务器操作系统一般指的是安装在大型计算机上的操作系统&#xff0c;比如Web服务器、应用服务器和数据库服务器等&#xff0c;是企业IT系统的基础架构平台&#xff0c;也是按应用领域划分的三类操作系统之一。同时服务器操作系统也可以安装在个人电脑上。 服务器操作系统分类 …

WSL Ubuntu 22.04.2 LTS 安装paddlepaddle-gpu==2.5.1踩坑日记

环境是wsl的conda环境。 使用conda安装paddlepaddle-gpu: conda install paddlepaddle-gpu2.5.1 cudatoolkit11.7 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/ -c conda-forge 等待安装... 报错处理&#xff1a; (1)PreconditionNotMetError: Cannot lo…

excel管理接口测试用例

闲话休扯&#xff0c;上需求&#xff1a;自动读取、执行excel里面的接口测试用例&#xff0c;测试完成后&#xff0c;返回错误结果并发送邮件通知。 分析&#xff1a; 1、设计excel表格 2、读取excel表格 3、拼接url&#xff0c;发送请求 4、汇总错误结果、发送邮件 开始实现…

LeetCode24.两两交换链表中的节点

这道题还是比较简单&#xff0c;我看完题目的想法就是借助一个第三变量&#xff0c;来改变两个节点的指针的指向&#xff0c;比如我要改变1和2节点的指向&#xff0c;我用h1表示节点1&#xff0c;我创建一个新的节点ans&#xff0c;先拿ans指向节点2&#xff08;ans.next h1.n…

【面试高频题】难度 1/5,经典树的搜索(多语言)

题目描述 这是 LeetCode 上的 「109. 有序链表转换二叉搜索树」 &#xff0c;难度为 「中等」 Tag : 「二叉树」、「树的搜索」、「分治」、「中序遍历」 给定一个单链表的头节点 head&#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为高度平衡的二叉搜索树。 本题中&…

软件工程与计算(十四)详细设计中面向对象方法下的模块化

一.面向对象中的模块 1.类 模块化是消除软件复杂度的一个重要方法&#xff0c;每个代码片段相互独立&#xff0c;这样能够提高可维护性。在面向对象方法中&#xff0c;代码片段最重要的类&#xff0c;整个类的所有代码联合起来构成独立的代码片段。 模块化希望代码片段由两部…

CSS的布局 Day03

一、显示模式&#xff1a; 网页中HTML的标签多种多样&#xff0c;具有不同的特征。而我们学习盒子模型、使用定位和弹性布局把内容分块&#xff0c;利用CSS布局使内容脱离文本流&#xff0c;使用定位或弹性布局让每块内容摆放在想摆放的位置&#xff0c;让网站页面布局更合理、…

Android ViewBinding和DataBinding功能作用区别

简述 ViewBinding和DataBinding都是用于在 Android 应用程序中处理视图的工具&#xff0c;但它们有不同的作用和用途。 ViewBinding: ViewBinding 是 Android Studio 的一个工具&#xff0c;用于生成一个绑定类&#xff0c;能够轻松访问 XML 布局文件中的视图。ViewBinding 为…

ubuntu16.04安装vscode遇到的code 依赖于 libnss3 (>= 2:3.30)解决

ubuntu16.04安装vscode遇到的code 依赖于 libnss3 (> 2:3.30)&#xff1b;然而&#xff1a;系统中 libnss3:amd64 的版本为 2:3.28.4-0ubuntu0.16.04.14解决方法 一开始下载了最新版本的vscode&#xff0c;安装时出现了上面的错误状况&#xff0c;最新版本的依赖库版本过低的…

Idea怎么配置Maven才能优先从本地仓库获取依赖

网上的方法 : 在设置中搜索 Runner ,在VM Option中设置参数 -DarchetypeCataloginternal删除 解压后的依赖包中的 _remote.repositories m2e-lastUpdated.properties *.lastUpdated 文件。 上边都没有效果 最终的解决方法&#xff0c;修改maven配置文件settings.xml 主要两个…

LeetCode讲解篇之198. 打家劫舍

LeetCode讲解篇之198. 打家劫舍 文章目录 LeetCode讲解篇之198. 打家劫舍题目描述题解思路题解代码 题目描述 题解思路 该问题可以通过递推来完成 递推公式&#xff1a; 前n间房的最大金额 max&#xff08;前n-1间房的最大金额&#xff0c; 前n-2间房的最大金额第n-1间房的最…

MySQL学习(四)——事务与存储引擎

文章目录 1. 事务1.1 概念1.2 事务操作1.2.1 未设置事务1.2.2 控制事务 1.3 事务四大特性1.4 并发事务问题1.5 事务隔离级别 2. 存储引擎2.1 MySQL体系结构2.2 存储引擎2.3 存储引擎的特点2.3.1 InnoDB2.3.2 MyISAM2.3.3 Memory2.3.4 区别和比较 1. 事务 1.1 概念 事务 是一组…

深度分析c+引用的本质以及引用与指针的区别

文章目录 引用的概念引用的定义引用的特性引用的权限问题引用的使用方式引用作参数引用作返回值指针的本质引用和指针的区别 引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用…

C语言——二周目——数据在内存中的存储

目录 一、整数的存储方式 二、浮点数的存储方式 一、整数的存储方式 因为CPU只有加法器&#xff0c;所以对于整型来说&#xff0c;数据在内存中通常采用补码的方式进行储存。 在这里复习一下原码、反码、补码。 正数和无符号数的原码、反码、补码相同&#xff1b; 负数的原…

depcheck检查项目依赖的安装情况-帮你解决各种项目运行灵异事件

depcheck检查项目缺失的依赖 depcheck介绍与安装介绍安装 depcheck使用基础使用注意 进阶使用 删除多余的依赖注意 depcheck介绍与安装 介绍 工作中&#xff0c;以下的场景恐怕大家都有经历过&#xff1a; 从代码仓库上面 clone 的项目&#xff0c;自己本地一运行就报错… 用…

网工实验笔记:MQC原理与配置

一、概述 MQC&#xff08;Modular QoS Command-Line Interface&#xff0c;模块化QoS命令行&#xff09;是指通过将具有某类共同特征的数据流划分为一类&#xff0c;并为同一类数据流提供相同的服务&#xff0c;也可以对不同类的数据流提供不同的服务。 MQC三要素 流分类&am…

流量代理——正向代理

流量代理 正向代理和反向代理 正向代理就是客户端和其他所有服务器&#xff08;重点&#xff1a;所有&#xff09;的代理者。 反向代理是客户端和所要代理的服务器之间的代理。 流量转发工具 需要放在拿到shell的服务器上可使用 lcx&#xff1a;端口流量转发&#xff0c;不具…

Motorola IPMC761 使用边缘TPU加速神经网络

Motorola IPMC761 使用边缘TPU加速神经网络 人工智能(AI)和机器学习(ML)正在塑造和推进复杂的自动化技术解决方案。将这些功能集成到硬件中&#xff0c;解决方案可以识别图像中的对象&#xff0c;分析和检测模式中的异常或找到关键短语。这些功能对于包括但不限于自动驾驶汽车…

Elasticsearch系列组件:Logstash强大的日志管理和数据分析工具

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…