【C语言】标准IO

ops/2025/1/14 17:31:56/

目录

1. 什么是标准IO

1.1概念

1.2特点

1.3 操作

2. 缓存区

3. 函数接口

3.1打开文件fopen

3.2关闭文件

3.3读写文件操作

3.3.1每次读写一个字符:fgetc()、fputc()

a. 针对文件

b. 针对终端

3.3.2 每次一串字符的读写fgets()和fputs()

c. 针对终端

d. 针对文件

3.1.3二进制读写fread()和fwrite()

a. 针对终端

b. 针对文件

3.4 其他操作

3.4.1 重定向流到文件freopen

3.4.2 文件定位操作


 

1. 什么标准IO

1.1概念

标准IO:在C库中定义的一组专门用于输入输出的函数。

1.2特点

(1) 通过缓冲机制减少系统调用,提高效率。

系统调用:内核向上提供的一组接口。

例如:从硬盘读1KB数据,每次只能读1B。

(2) 围绕着流(stream)进行操作,流用FILE*来描述。

注意: vi用ctags索引使用:

1) vi -t 查找名称

输入前面序号,回车。

2) 继续追踪:

将光标定位到要追踪的内容,ctrl+]

回退:ctrl+t

3)跳转到上次位置:ctrl+o

跳转到下次位置:ctrl+i

(3) 标准IO默认打开三个流,sdtin(标准输入)、stdout(标准输出)、stderr(标准错误)流。

1.3 操作

打开文件:fopen

关闭文件:fclose

读写操作:fgetc、fputc、fgets、fputs、fread、fwrite

其他:freopen、rewind、fseek

2. 缓存区

(1)全缓存:和文件相关的

(2)行缓存:和终端相关

刷新缓存区的条件:

● \n

● 程序正常退出

● 强制刷新:fflush

● 缓存区满

#include<stdio.h>int main(int argc, char const *argv[])
{// printf("hello world\n"); //\n不光换行还可以刷新缓存区,将缓存区内容刷新到终端。printf("hello world");fflush(NULL); //强制刷新缓存区while(1);return 0;
}

(3)不缓存,没有缓存,标准错误。

综上:当我们每次要打印数据时,并不是将数据直接发送给标准输出设备,也就是并直接发送给显示器,而是将要打印的数据先存放到缓存区,当缓冲存数据满时,或者遇到\n,或者程序结束时,或者手动刷新缓存区时,缓冲区才会把数据传输到标准输出设备中,也就是显示器中进行输出。

练习:计算标准输出缓存区大小KB

方法一:利用循环打印展示

方法二:利用结构体指针stdout(流指针FILE*类型)

#include<stdio.h>int main(int argc, char const *argv[])
{printf("buf:"); //不进行输出的话缓存区不会开辟,所以要先加一句打印为了让标准IO开辟输出缓存区。printf("%d\n",stdout->_IO_buf_end - stdout->_IO_buf_base);return 0;
}

得到1024B

3. 函数接口

3.1打开文件fopen

FILE *fopen(const char *path, const char *mode);
功能:打开文件
参数:path:打开的文件路径mode:打开的方式r:只读,当文件不存在时报错,文件流定位到文件开头r+:可读可写,当文件不存在时报错,文件流定位到文件开头w:只写,文件不存在创建,存在则清空w+:可读可写,文件不存在创建,存在则清空a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
返回值:成功:文件流失败:NULL,并且会设置错误码

3.2关闭文件

int fclose(FILE *stream);
功能:关闭文件
参数:stream:文件流
#include <stdio.h>int main(int argc, char const *argv[])
{FILE *fp;//1.打开文件fp = fopen("test.c", "r");if (NULL == fp){perror("fopen err");//while(1);//标准错误是不缓存,所以会直接显示到终端return -1;}printf("fopen success\n");//2. 关闭文件fclose(fp);return 0;
}

3.3读写文件操作

3.3.1每次读写一个字符fgetc()fputc()

每次读一个字符fgetc()int  fgetc(FILE * stream);
功能:从文件中读取一个字符,并将当前文件指针位置向后移动一个字符。
参数:stream:文件流
返回值:成功:读到的字符的ASCII失败或读到文件末尾:EOF(-1)每次写一个字符fputc()int fputc(int c, FILE *stream);
功能:向文件中写入一个字符, 成功写入后文件指针会自动向后移动一个字节位置。
参数:c:要写的字符stream:文件流
返回值:成功:写的字符的ASCII失败:EOF(-1)
a. 针对文件
#include <stdio.h>int main(int argc, char const *argv[])
{FILE *fp;char ch;//打开文件fp = fopen("test.c", "r+");if (NULL == fp){perror("fopen err");return -1;}printf("fopen success\n");//针对文件读写操作//读操作ch = fgetc(fp);printf("%c %d\n", ch, ch); //h 104ch = fgetc(fp);printf("%c %d\n", ch, ch); //e 104ch = fgetc(fp);printf("%c %d\n", ch, ch); //l 108//写操作fputc('a', fp); //写个a(需要注意要有可写权限)fputc(98, fp);  //写个b,此时文件中是helabch = fgetc(fp);printf("%c %d\n", ch, ch); //EOF -1 (因为已经到末尾了)//关闭文件fclose(fp);return 0;
}
b. 针对终端
#include <stdio.h>int main(int argc, char const *argv[])
{char ch = fgetc(stdin);printf("%c %d\n", ch, ch);fputc('o', stdout);fputc(10, stdout);return 0;
}

补充feofferror

int  feof(FILE * stream);
功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符。
返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0。
#include <stdio.h>int main(int argc, char const *argv[])
{FILE *fp;char ch;//打开文件fp = fopen("test.c", "r+");if (NULL == fp){perror("fopen err");return -1;}printf("fopen success\n");//针对文件读写操作//读操作ch = fgetc(fp);printf("%c %d\n", ch, ch); //h 104ch = fgetc(fp);printf("%c %d\n", ch, ch); //e 104ch = fgetc(fp);printf("%c %d\n", ch, ch); //l 108//写操作fputc('a', fp); //写个a(需要注意要有可写权限)fputc(98, fp);  //写个b,此时文件中是helab//前面和之前例子一样,然后判断是否末尾if (feof(fp)){printf("end1\n");   //因为后面还有一个EOF,所以判断末尾不成功。不会打印end1。return -1;}ch = fgetc(fp);printf("%c %d\n", ch, ch); //EOF -1 if (feof(fp)){printf("end2\n");    //会打印出来,因为后面没有内容,已经到末尾了。return -1;}//关闭文件fclose(fp);return 0;
}

int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值。如果没有出错,返回0。
#include <stdio.h>int main(int argc, char const *argv[])
{FILE *fp;char ch;//打开文件fp = fopen("test.c", "w");if (NULL == fp){perror("fopen err");return -1;}printf("fopen success\n");//因为权限为只写,所以读操作有错误。ch = fgetc(fp);if (ferror(fp)){printf("err exist\n");return -1;}//关闭文件fclose(fp);return 0;
}

练习:cat 文件名

#include <stdio.h>/* cat 文件名:查看文件内容,显示到终端。步骤:1.打开文件2.循环用fgetc获取文件内容3.当读到文件末尾标志EOF时结束4.将读取文件内容用fputc打印到终端5.关闭文件
*/
int main(int argc, char const *argv[])
{char ch;if (argc != 2){printf("pls input %s <filename>\n", argv[0]);return -1;}//1. 打开文件FILE *fp = fopen(argv[1], "r");if (NULL == fp){perror("fopen err");return -1;}//2. 循环用fgetc获取文件内容//3. 当读到文件末尾标志EOF时结束while ((ch = fgetc(fp)) != EOF) //当读到内容不是EOF就进入循环{//4.将读到内容用fputc打印到终端fputc(ch, stdout);}//5. 关闭文件return 0;
}

3.3.2 每次一串字符的读写fgets()和fputs()

char * fgets(char *s,  int size,  FILE * stream);
功能:从文件中每次读取一行字符串
参数:    s:存放字符串的地址size:一次读取的字符个数stream:文件流
返回值:成功:s的地址失败或读到文件末尾:NULL
特性:  每次实际读取的字符个数为size-1个,会在末尾自动添加\0每次读一行,遇到\n或者到达文件末尾后不再继续读下一行并把它存储在s所指向的字符串内。int fputs(const char *s, FILE *stream);
功能:向文件中写字符串
参数:s:要写的内容stream:文件流
返回值:成功:非负整数失败:EOF
c. 针对终端
#include <stdio.h>int main(int argc, char const *argv[])
{char buf[32]="";//针对终端//输入操作fgets(buf,sizeof(buf),stdin); //终端输入的是hello\nprintf("buf:%s\n",buf); //此时buf里面存的内容是hello\n\0//输出操作fputs(buf,stdout);      //把buf中的内容hello\n\0输出到终端fputs("world",stdout);  //输出world\0到终端return 0;
}

注意:一定会留一个位置给\0

d. 针对文件
#include <stdio.h>int main(int argc, char const *argv[])
{char buf[32] = "";//打开文件FILE *fp = fopen("test.c", "r+");if (NULL == fp){perror("fopen err");return -1;}printf("fopen success\n");//针对文件读写操作//读操作fgets(buf, 32, fp); //hello\n\0fputs(buf, stdout);fgets(buf, 32, fp); //world\n\0fputs(buf, stdout);fgets(buf, 32, fp); //hahaha\0fputs(buf, stdout);fputs("666",fp);    //向文件写666\0//关闭文件fclose(fp);return 0;
}

注意:如果到达末尾则返回NULL,继续输出的还是上一次buf中保留的内容。

练习:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)

思路:打开文件,循环读文件fgets(buf,32,fp);

#include <stdio.h>
#include <string.h>int main(int argc, char const *argv[])
{char buf[32] = "";int n=0;if(argc!=2){printf("pls input:%s <filename>\n",argv[0]);return -1;}FILE *fp = fopen(argv[1], "r");if (NULL == fp){perror("fopen err");return -1;}printf("fopen success\n");while (fgets(buf,32,fp)!=NULL){if(buf[strlen(buf)-1]=='\n')n++; }printf("%d %s\n",n,argv[1]);fclose(fp);return 0;
}

注意:wc-l也会少一行,因为最后一行没有换行

3.1.3二进制读写fread()fwrite()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素(将二进制数据从文件读出)参数:  ptr :是一个指针,是存放数据的存储空间的起始地址,用来存放读取元素size :元素大小  sizeof(元素数据类型)nmemb :读取元素的个数stream :要读取的文件流
返回值:成功:读取的元素的个数读到文件尾或失败: 0size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:将二进制数据写入文件
参数: ptr :是一个指针,保存要输出数据的空间的地址。size :要写入的字节数 sizeof(数据类型)nmemb : 要进行写入元素的个数strem: 目标文件流指针
返回值:成功:写的元素个数失败 :-1
a. 针对终端
char buf[32];
fread(buf,1,10,stdin);
printf("%s\n",buf);
fwrite(buf,1,10,stdout);

因为读写了10个单位char类型元素

b. 针对文件
#include <stdio.h>
#include <string.h>int main(int argc, char const *argv[])
{FILE *fp;float arr[3]={1.2,2.3,3.4};float data[3]={0};fp=fopen(argv[1],"r+");if (fp==NULL){perror("err");return -1;}fwrite(arr,4,3,fp);rewind(fp);//定位到文件开头fread(data,4,3,fp);printf("%f %f %f\n",data[0],data[1],data[2]);return 0;
}

文件定位操作:rewind(FILE *fp);

练习:

2. 题目要求:编程读写一个文件test.txt,每隔1秒向文件中写入一行录入时间的数据,类似这样:

1, 2007-7-30 15:16:42

2, 2007-7-30 15:16:43

该程序应该无限循环,直到按Ctrl-C中断程序。

再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如:

1, 2007-7-30 15:16:42

2, 2007-7-30 15:16:43

3, 2007-7-30 15:19:02

4, 2007-7-30 15:19:03

5, 2007-7-30 15:19:04

思路:

1. 打开文件fopen,循环往文件写内容

2. 每隔1s写入一行,sleep(1);

3. 计算文件行数,wc -l

4. 计算当前时间,转换成年月日、时分秒,time,localtime

man 2 time

time_t time(time_t *t);

如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。

5. 字符串拼接函数:strcpy/strcat(dest, src)、sprintf、fprintf

fprintf:

格式化输出到流(stream)文件中,返回值是输出的字符数,发生错误时返回一个负值.

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

sprintf:

格式化输出发送到buffer(缓冲区)中.返回值是写入的字符数量.

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

思路:

FILE *fp = fopen();

//计算文件行数

while(fgets()!=NULL)

if(buf[] == '\n')

n++;

//往文件中写入内容

while(1)

{

//计算时间

time_t t = time();

struct tm *tm = localtime(t);

fprintf(fp, "%d,%d");

sleep(1);

}

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>int main(int argc, char const *argv[])
{FILE *fp;char buf[32] = "";int n = 0;time_t t;struct tm *tm;fp = fopen(argv[1], "a+");if (NULL == fp){perror("fopen err");return -1;}while (fgets(buf, 32, fp) != NULL){if (buf[strlen(buf) - 1] == '\n')n++;}while (1){time(&t); //t=time(NULL);tm = localtime(&t);fprintf(fp, "%d,%d-%d-%d %d:%d:%d\n", ++n, tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); //月份从1月开始,tm结构体中月份是0-11所以+1fflush(NULL); //全缓存,写文件里面没有\n刷新,所以要手动刷新。sleep(1);}fclose(fp);return 0;
}

3.4 其他操作

3.4.1 重定向流到文件freopen

freopen()用于将指定的流重定向到打开的文件

FILE *freopen(const char *path, const char *mode, FILE *stream);
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径mode:打开文件的方式(同fopen)fp:文件流指针
返回值:成功:返回文件流指针失败:NULL
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("hello\n");//将标准输出流重定向到打开的test.txt文件中freopen("test.txt","w+",stdout);printf("world\n");//将标准输出流重新重定向到终端文件/dev/ttyfreopen("/dev/tty","r+",stdout);printf("hahaha\n");return 0;
}

3.4.2 文件定位操作

void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移whence:相对位置:SEEK_SET:相对于文件开头SEEK_CUR:相对于文件当前位置SEEK_END:相对于文件末尾
返回值:成功:0失败:-1   注:当打开文件的方式为a或a+时,fseek不起作用     
补充:其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.简言之:
fseek(fp,100,0); 把fp指针移动到离文件开头100字节处.fseek(fp,100,1); 把fp指针移动到离文件当前位置100字节处;
ffseek(fp,-100,2);把fp指针退回到离文件结尾100字节处。long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>int main(int argc, char const *argv[])
{FILE *fp = fopen("test.txt","w+");if (NULL==fp){perror("err");return -1;}//相对于开头往后10个,写一个afseek(fp,10,SEEK_SET);fputc('a',fp);//相等于当前位置往后5个,写一个hellofseek(fp,5,SEEK_CUR);fputs("hello",fp);//想对于最后往前1个,写一个ifseek(fp,-1,SEEK_END);fputc('i',fp);long l = ftell(fp); //计算文件位置指针所在位置printf("%ld\n",l);  //21return 0;
}

笔试题:

1. 想对一个文本文件的尾部追加写入,应当在fopen何中使用的文件操作方式指示符号为 ()(杭州快越科技笔试题)

A. r B.wb C. a D.w+

2. fseek(1, 2, 3); 这个函数是什么作用,三个参数分别是什么意思?(深圳元征信息科技)

3. 函数调用语句:fseek (fp,-10L,2);的含义是

A 将文件位置指针从文件未尾处向文件头的方向移动10个字节

B 将文件位置指针从当前位置向文件头的方向移动10个字节

C 将文件位置指针从当前位置向文件未尾方向移动10个字节

总结:为什么用标准IO?

1. 因为读写文件通常是大量的数据,这时使用标准IO可以大大减少系统调用的次数,从而提高效率。

2. 为了保证可移植性

关于缓存区: 库函数的缓冲区对于库函数,如果标准输出连到终端设备(直接输出到屏幕),则它是行缓冲的(遇到回车换行符或者是缓冲区满了才输出);否则(输出到文件)是全缓冲的(缓冲区填满或者是程序运行结束了才输出)。程序运行结束时,会刷新所有的缓冲区。


http://www.ppmy.cn/ops/150061.html

相关文章

基于微信小程序的书籍销售系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

PHP RCE

靶场搭建 生成容器 docker run -p 18022:22 -p 18080:80 -p 18081:81 -p 18082:82 -p 18085:85 -i -t mcc0624/cmd:latest bash -c /etc/rc.local; /bin/bash 管理网站 http://<IP>:18085/CZKJ2022 用户名&#xff1a;admin 密码&#xff1a;Pssw0rd SSH 用户名root 密…

深度学习——pytorch基础入门

一、张量 在PyTorch中&#xff0c;张量是PyTorch中最基本的数据结构。张量可以看作是一个多维数组&#xff0c;可以在GPU上加速运算。PyTorch的张量和Numpy的数组非常类似&#xff0c;但是与Numpy不同的是&#xff0c;PyTorch的张量可以自动地在GPU上进行加速计算。 PyTorch中的…

解锁 C# 与 LiteDB 嵌入式 NoSQL 数据库

一、开篇&#xff1a;邂逅 C# 与 LiteDB 新世界 在当今的软件开发领域&#xff0c;数据管理如同建筑的基石&#xff0c;而选择一款合适的数据库则是项目成功与否的关键因素之一。对于 C# 开发者来说&#xff0c;面对琳琅满目的数据库选项&#xff0c;如何抉择常常令人头疼。今…

【Linux】Linux常见指令(下)

个人主页~ Linux常见命令&#xff08;上&#xff09;~ 初识Linux 一、Linux基本命令11、cat命令12、more指令13、less指令14、head指令15、tail指令16、时间相关的指令&#xff08;1&#xff09;date指令&#xff08;2&#xff09;cal指令 17、find指令18、grep指令19、压缩相…

vue3+vite+ts集成第三方js

npm run dev可以正常运行和测试。但是npm run build会报错。 要实现引入静态js&#xff0c;避免使用全局变量报错。 1. HTML 引入第三方 JS 在你的 HTML 文件中&#xff0c;通过 <script> 标签引入一个本地第三方 JS 文件&#xff0c;例如&#xff1a; <script sr…

【C++经典例题】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a; 期待您的关注 题目描述&#xff1a; 原题链接&#xff1a; 求123...n_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; …

深入探讨 Vue.js 的动态组件渲染与性能优化

Vue.js 作为一款前端领域中备受欢迎的渐进式框架&#xff0c;以其简单优雅的 API 和灵活性受到开发者的喜爱。在开发复杂应用时&#xff0c;动态组件渲染是一项极其重要的技术&#xff0c;它能够在页面中动态地加载或切换组件&#xff0c;从而显著提升应用的灵活性与用户体验。…