Linux自学day20-流的输入与输出
一 fputc、fgetc的用法
1.1用fputc和fgetc完成文件内容的拷贝,代码如下:
// 函数功能:将源文件的内容复制到目标文件
// 参数:pdstfilename - 目标文件名的指针,psrcfilename - 源文件名的指针
// 返回值:成功返回0,失败返回-1
int copy_file_content(const char *pdstfilename, const char *psrcfilename)
{
FILE *fsrc = NULL; // 定义一个文件指针,用于指向源文件
FILE *fdst = NULL; // 定义一个文件指针,用于指向目标文件
char ch = 0; // 用于存储从源文件读取的字符
// 以只读模式打开源文件
fsrc = fopen(psrcfilename, "r");
if (NULL == fsrc)
{
ERR_MSG("打开源文件失败"); // 若打开源文件失败,输出错误信息
goto err_open_srcfile; // 跳转到错误处理标签
}
// 以写入模式打开目标文件
fdst = fopen(pdstfilename, "w");
if (NULL == fdst)
{
ERR_MSG("打开目的文件失败"); // 若打开目标文件失败,输出错误信息
goto err_open_dstfile; // 跳转到错误处理标签
}
while (1)
{
// 从源文件中读取一个字符
ch = fgetc(fsrc);
// 如果读取到文件末尾(EOF表示文件结束符),则退出循环
if (EOF == ch)
{
break;
}
// 将读取到的字符写入目标文件
fputc(ch, fdst);
}
// 关闭源文件和目标文件
fclose(fsrc);
fclose(fdst);
return 0; // 复制成功,返回0
// 打开目标文件失败的错误处理
err_open_dstfile:
fclose(fsrc); // 关闭已打开的源文件
// 打开源文件失败的错误处理
err_open_srcfile:
return -1; // 复制失败,返回-1
}
// 主函数,程序入口
int main(int argc, const char **argv)
{
char srcfilename[32] = {0}; // 存储源文件名的数组
char dstfilename[32] = {0}; // 存储目标文件名的数组
int ret = 0; // 存储函数返回值
// argv[0] = ./a.out ,程序本身的名称
// argv[1] = srcfilename ,第一个命令行参数,即源文件名
// argv[2] = dstfilename ,第二个命令行参数,即目标文件名
if (argc != 3)
{
printf("usage:./a.out srcfilename dstfilename\n"); // 若参数个数不正确,输出使用方法
printf("command: this command is used to copy srcfilename content to dstfilename\n");
return -1; // 参数错误,返回-1
}
// 调用copy_file_content函数进行文件复制
ret = copy_file_content(argv[2], argv[1]);
if (ret != 0)
{
printf("拷贝失败!\n"); // 若复制失败,输出错误信息
return -1; // 复制失败,返回-1
}
printf("拷贝成功!\n"); // 复制成功,输出成功信息
return 0; // 程序正常结束,返回0
}
1.2fgetc
函数的用法
fgetc
函数用于从指定的文件流中读取一个字符。其原型为:
int fgetc(FILE *stream);
stream
是指向文件的指针,表示要读取的文件流。- 函数返回值:如果读取成功,返回读取到的字符(以
int
类型返回);如果到达文件末尾或发生错误,返回EOF
(EOF
是一个宏,通常定义为-1
)。
1.3fputc
函数的用法
fputc
函数用于将一个字符写入指定的文件流。其原型为:
int fputc(int c, FILE *stream);
c
是要写入的字符(以int
类型传入)。stream
是指向文件的指针,表示要写入的文件流。- 函数返回值:如果写入成功,返回写入的字符;如果发生错误,返回
EOF
。
二 fputs、fgets的用法
2.1 用fputs、fgets完成文件内容的拷贝,代码如下:
// 函数功能:将源文件的内容复制到目标文件
// 参数:pdstfile - 目标文件的路径
// psrcfile - 源文件的路径
// 返回值:0 - 复制成功,-1 - 复制失败
int copy_file_content(const char *pdstfile, const char *psrcfile)
{
FILE *psrc = NULL; // 源文件的文件指针,初始化为NULL
FILE *pdst = NULL; // 目标文件的文件指针,初始化为NULL
char *pret = NULL; // fgets函数的返回值,用于判断是否读取到数据
char tmpbuff[1024] = {0}; // 临时缓冲区,用于存储从源文件读取的数据
// 以只读模式打开源文件
psrc = fopen(psrcfile, "r");
if (NULL == psrc)
{
ERR_MSG("打开文件失败"); // 打开源文件失败,输出错误信息
goto err_open_srcfile; // 跳转到错误处理标签
}
// 以写入模式打开目标文件
pdst = fopen(pdstfile, "w");
if (NULL == pdst)
{
ERR_MSG("打开文件失败"); // 打开目标文件失败,输出错误信息
goto err_open_dstfile; // 跳转到错误处理标签
}
// 循环读取源文件内容并写入目标文件
while (1)
{
// 从源文件中读取一行数据,最多读取 sizeof(tmpbuff)-1 个字符
pret = fgets(tmpbuff, sizeof(tmpbuff), psrc);
if (NULL == pret)
{
break; // 读取结束,跳出循环
}
// 将读取到的数据写入目标文件
fputs(tmpbuff, pdst);
}
// 关闭源文件和目标文件
fclose(psrc);
fclose(pdst);
return 0; // 复制成功,返回0
err_open_dstfile:
fclose(psrc); // 关闭已打开的源文件
err_open_srcfile:
return -1; // 复制失败,返回-1
}
// 主函数,程序入口
int main(int argc, const char **argv)
{
int ret = 0; // 用于存储 copy_file_content 函数的返回值
// 检查命令行参数个数是否正确
if (argc != 3)
{
printf("Usage:./a.out srcfile dstfile\n"); // 输出使用说明
return -1; // 参数个数错误,返回-1
}
// 调用 copy_file_content 函数进行文件复制
ret = copy_file_content(argv[2], argv[1]);
if (ret != 0)
{
printf("拷贝失败!\n"); // 复制失败,输出错误信息
}
else
{
printf("拷贝成功!\n"); // 复制成功,输出成功信息
}
return 0; // 程序正常结束,返回0
}
2.2 fgets
函数
- 函数原型:
char *fgets(char *str, int n, FILE *stream);
- 功能:从指定的文件流中读取一行数据,最多读取
n-1
个字符,并将读取到的数据存储到str
指向的缓冲区中。读取的数据会自动在末尾添加'\0'
作为字符串的结束符。 - 参数:
str
:指向存储读取数据的缓冲区的指针。n
:要读取的最大字符数(包括'\0'
)。stream
:指向要读取的文件的文件指针。
- 返回值:
- 如果读取成功,返回
str
的地址。 - 如果到达文件末尾或出错,返回
NULL
。
- 如果读取成功,返回
2.3 fputs
函数
- 函数原型:
int fputs(const char *str, FILE *stream);
- 功能:将
str
指向的字符串写入到指定的文件流中。 - 参数:
str
:要写入的字符串。stream
:指向要写入的文件的文件指针。
- 返回值:
- 如果写入成功,返回非负整数。
- 如果出错,返回
EOF
。
三 fputc、fgetc和fputs、fgets的异同
在文件内容拷贝场景中,通常有两种常见的实现思路:一种是按字符拷贝(使用 fgetc
和 fputc
函数),另一种是按行拷贝(使用 fgets
和 fputs
函数)。
3.1 相同点
1. 最终目的相同
两种方法的核心目的都是将一个文件(源文件)的内容复制到另一个文件(目标文件)中,完成文件内容的拷贝操作。
2. 基本操作流程类似
都需要进行文件的打开、内容读取、内容写入以及文件关闭这些基本操作。以按字符拷贝和按行拷贝为例,一般都遵循以下步骤:
- 打开源文件和目标文件。
- 从源文件读取内容。
- 将读取的内容写入目标文件。
- 关闭源文件和目标文件。
3. 错误处理机制类似
在文件打开、读取和写入过程中,都需要进行错误检查和处理。如果文件打开失败、读取出现错误或者写入失败,都需要相应地处理错误情况,以保证程序的健壮性。
3.2 不同点
1. 读取和写入单位不同
- 按字符拷贝(
fgetc
和fputc
):每次从源文件读取一个字符,然后将这个字符写入目标文件。这种方式逐字符处理文件内容,适用于处理任意类型的文件,包括二进制文件和文本文件。 - 按行拷贝(
fgets
和fputs
):每次从源文件读取一行文本,然后将这一行文本写入目标文件。这种方式更适用于处理文本文件,因为它是以换行符作为行的分隔标志。
2. 性能差异
- 按字符拷贝:由于每次只处理一个字符,会频繁进行文件的读取和写入操作,系统开销较大,尤其是对于大文件,性能相对较低。
- 按行拷贝:一次性读取和写入一行数据,减少了文件操作的次数,系统开销相对较小,在处理文本文件时性能通常优于按字符拷贝。
3. 适用场景不同
- 按字符拷贝:适用于所有类型的文件,因为它不依赖于特定的文本格式,可以准确地复制任何文件内容,包括二进制文件(如图片、视频等)。
- 按行拷贝:主要适用于文本文件,因为它假设文件内容是以行为单位组织的。如果用于处理二进制文件,可能会因为换行符的处理问题导致数据损坏。
4. 代码复杂度
- 按字符拷贝:代码逻辑相对简单,只需要一个循环不断读取和写入字符即可。
- 按行拷贝:需要处理行缓冲区的大小问题,并且要确保读取的行不会超出缓冲区的范围,代码相对复杂一些。
四 fprintf、fscanf的用法
4.1 fprintf的用法
fprintf
是一个用于将格式化的数据写入文件的函数,其原型为:
int fprintf(FILE *stream, const char *format, ...);
-
FILE *stream
:指向要写入的文件的指针。 -
const char *format
:格式化字符串,类似于printf
中的格式化字符串,用于指定输出的格式。 -
...
:可变参数列表,根据格式化字符串中的格式说明符提供相应的参数。
4.2 fscanf的用法
fscanf
是一个用于从文件中读取格式化数据的函数,其原型为:
int fscanf(FILE *stream, const char *format, ...);
FILE *stream
:指向要读取的文件的指针。const char *format
:格式化字符串,类似于scanf
中的格式化字符串,用于指定输入的格式。...
:可变参数列表,根据格式化字符串中的格式说明符提供相应的变量地址,用于存储读取到的数据。
// argc是命令行参数的数量,argv是指向命令行参数字符串数组的指针
int main(int argc, const char **argv)
{
FILE *fp = NULL; // 定义一个文件指针fp,并初始化为NULL
char tmpbuff[1024] = {0}; // 定义一个字符数组tmpbuff,用于存储从文件中读取的字符串,初始化为全0
int num = 0; // 定义一个整型变量num,用于存储从文件中读取的整数,初始化为0
double d = 0; // 定义一个双精度浮点型变量d,用于存储从文件中读取的浮点数,初始化为0
// 以只读模式打开名为"a.txt"的文件
fp = fopen("a.txt", "r");
if (NULL == fp) // 检查文件是否成功打开
{
ERR_MSG("fail fopen"); // 如果文件打开失败,输出错误信息
return -1; // 返回错误码-1
}
// 从文件中读取一个字符串到tmpbuff中,遇到空格或换行符停止读取
fscanf(fp, "%s", tmpbuff);
printf("tmpbuff = %s\n", tmpbuff); // 打印读取到的字符串
// 再次从文件中读取一个字符串到tmpbuff中,遇到空格或换行符停止读取
fscanf(fp, "%s", tmpbuff);
printf("tmpbuff = %s\n", tmpbuff); // 打印读取到的字符串
fgetc(fp); // 从文件中读取一个字符,用于跳过换行符
// 从文件中读取一个整数,格式为"num = 数字",将数字存储到num变量中
fscanf(fp, "num = %d", &num);
printf("num = %d\n", num); // 打印读取到的整数
fgetc(fp); // 从文件中读取一个字符,用于跳过换行符
// 从文件中读取一个双精度浮点数,格式为"d = 浮点数",将浮点数存储到d变量中
fscanf(fp, "d = %lf", &d);
printf("d = %lf\n", d); // 打印读取到的浮点数
fclose(fp); // 关闭文件
#if 0
FILE *fp = NULL; // 定义一个文件指针fp,并初始化为NULL
int num = 100; // 定义一个整型变量num,初始化为100
double d = 3.14; // 定义一个双精度浮点型变量d,初始化为3.14
// 以写入模式打开名为"a.txt"的文件
fp = fopen("a.txt", "w");
if (NULL == fp) // 检查文件是否成功打开
{
ERR_MSG("fopen failed"); // 如果文件打开失败,输出错误信息
return -1; // 返回错误码-1
}
// 将字符串"hello world\n"写入文件
fprintf(fp, "hello world\n");
// 将格式化的字符串"num = 数字\n"写入文件,其中数字为num的值
fprintf(fp, "num = %d\n", num);
// 将格式化的字符串"d = 浮点数\n"写入文件,其中浮点数为d的值
fprintf(fp, "d = %lf\n", d);
fclose(fp); // 关闭文件
#endif
return 0; // 程序正常结束,返回0
}
五 fwrite、fread的用法
5.1 fwrite
函数的用法
- 函数原型:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
- 功能:将数据从内存写入到文件中。
- 参数:
ptr
:指向要写入数据的内存地址的指针。size
:每个数据项的字节大小。count
:要写入的数据项的数量。stream
:指向要写入的文件的指针。
- 返回值:返回实际写入的数据项的数量。如果发生错误,返回值可能小于
count
。
5.2 fread
函数的用法
- 函数原型:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
- 功能:从文件中读取数据到内存中。
- 参数:
ptr
:指向用于存储读取数据的内存地址的指针。size
:每个数据项的字节大小。count
:要读取的数据项的数量。stream
:指向要读取的文件的指针。
- 返回值:返回实际读取的数据项的数量。如果到达文件末尾或发生错误,返回值可能小于
count
。如果返回值为 0,表示文件读取结束或发生错误。
5.3 写入和读取学生信息:
// 定义一个名为 student 的结构体,用于存储学生信息
typedef struct student
{
char name[32]; // 学生姓名,最多31个字符加上字符串结束符 '\0'
char sex; // 学生性别,用一个字符表示
int age; // 学生年龄
int score; // 学生成绩
} stu_t;
// 主函数,程序的入口点
int main(int argc, const char **argv)
{
stu_t s[5]; // 定义一个包含5个 stu_t 结构体的数组,用于存储从文件中读取的学生信息
FILE *fp = NULL; // 定义一个文件指针,初始化为 NULL
size_t nret = 0; // 用于存储 fread 函数的返回值,表示实际读取的对象数量
int i = 0; // 循环计数器
// 以只读模式打开名为 "a.txt" 的文件
fp = fopen("a.txt", "r");
if (NULL == fp)
{
ERR_MSG("fail to fopen"); // 如果文件打开失败,输出错误信息
return -1; // 程序返回 -1 表示错误
}
// 循环读取文件内容,直到读取结束
while (1)
{
// 从文件中读取最多5个 stu_t 结构体对象到数组 s 中
nret = fread(s, sizeof(stu_t), 5, fp);
if (0 == nret)
{
break; // 如果读取到的对象数量为0,表示文件读取结束,退出循环
}
printf("读到 %ld 个对象\n", nret); // 输出实际读取到的对象数量
// 遍历读取到的对象,打印每个学生的信息
for (i = 0; i < nret; i++)
{
printf("姓名:%s\n", s[i].name); // 打印学生姓名
printf("性别:%c\n", s[i].sex); // 打印学生性别
printf("年龄:%d\n", s[i].age); // 打印学生年龄
printf("成绩:%d\n", s[i].score); // 打印学生成绩
}
}
// 关闭文件
fclose(fp);
#if 0
// 定义一个包含3个 stu_t 结构体的数组,并初始化学生信息
stu_t s[3] = {
{"zhangsan", 'm', 19, 100},
{"lisi", 'f', 18, 90},
{"wager", 'm', 17, 60},
};
int i = 0; // 循环计数器
int cnt = 0; // 用于存储 fwrite 函数的返回值,表示实际写入的对象数量
FILE *fp = NULL; // 定义一个文件指针,初始化为 NULL
// 以写入模式打开名为 "a.txt" 的文件
fp = fopen("a.txt", "w");
if (NULL == fp)
{
ERR_MSG("fail to fopen"); // 如果文件打开失败,输出错误信息
return -1; // 程序返回 -1 表示错误
}
// 将数组 s 中的3个 stu_t 结构体对象写入文件
cnt = fwrite(s, sizeof(stu_t), 3, fp);
printf("写入 %d 个元素\n", cnt); // 输出实际写入的对象数量
// 逐个将数组 s 中的 stu_t 结构体对象写入文件
for (i = 0; i < 3; i++)
{
fwrite(s + i, sizeof(stu_t), 1, fp);
}
// 关闭文件
fclose(fp);
#endif
return 0; // 程序正常结束,返回 0
}
六 fseek、ftell的用法
6.1 fseek
函数的用法
fseek
函数用于设置文件指针的位置。其函数原型为:
int fseek(FILE *stream, long int offset, int whence);
stream
:指向要操作的文件的指针。offset
:偏移量,表示要移动的字节数。可以是正数(向前移动)、负数(向后移动)或 0。whence
:起始位置,有以下三个取值:SEEK_SET
:从文件开头开始偏移。SEEK_CUR
:从文件指针的当前位置开始偏移。SEEK_END
:从文件末尾开始偏移。
fseek
函数的返回值为 0 表示成功,非 0 表示失败。
6.2 ftell
函数的用法
ftell
函数用于获取文件指针的当前位置。其函数原型为:
long int ftell(FILE *stream);
stream
:指向要操作的文件的指针。
ftell
函数返回文件指针的当前位置,即从文件开头到当前位置的字节数。如果发生错误,返回 -1L。
6.3 用fseek、ftell获得文件大小:
// 函数功能:获取指定文件的长度
// 参数:pfilename - 指向文件名的指针
// 返回值:如果文件打开成功,返回文件的长度(字节数);如果文件打开失败,返回 -1
long get_file_len(char *pfilename)
{
FILE *fp = NULL; // 定义一个文件指针,初始化为 NULL
long len = 0; // 用于存储文件长度的变量,初始化为 0
// 以只读模式打开文件
fp = fopen(pfilename, "r");
// 检查文件是否成功打开,如果文件打开失败,fopen 返回 NULL
if (NULL == fp)
{
return -1; // 文件打开失败,返回 -1 表示错误
}
// 将文件指针移动到文件末尾
// fseek 函数用于移动文件指针,第一个参数是文件指针,第二个参数是偏移量,第三个参数是起始位置
// 这里偏移量为 0,起始位置为 SEEK_END,表示从文件末尾开始偏移 0 个字节,即移动到文件末尾
fseek(fp, 0, SEEK_END);
// 获取文件指针的当前位置,即文件的长度
// ftell 函数用于获取文件指针的当前位置,返回值是从文件开头到当前位置的字节数
len = ftell(fp);
// 关闭文件,释放文件资源
fclose(fp);
return len; // 返回文件的长度
}
// 主函数,程序的入口点
int main(int argc, const char **argv)
{
char filename[32] = {0}; // 定义一个字符数组,用于存储用户输入的文件名,初始化为 0
long len = 0; // 用于存储文件长度的变量,初始化为 0
// 提示用户输入文件名
printf("请输入文件名:\n");
// 读取用户输入的文件名,注意:gets 函数存在缓冲区溢出的风险,建议使用 fgets 替代
gets(filename);
// 调用 get_file_len 函数获取文件长度
len = get_file_len(filename);
// 输出文件的长度
printf("文件大小: %ld\n", len);
return 0; // 程序正常结束,返回 0
}