一、标准IO操作函数
1.fgets、fputs
int fputs(const char *s, FILE *stream);
功能描述:将字符串s写入stream指向的文件中
返回数:成功写入文件中数据的字节数
int puts(const char* s)
功能描述:将字符串s写入终端;
返回值:成功写入终端的数据的字节数
char *fgets(char *s, int size, FILE *stream);
功能:从stream指向的文件中,吸收最多size个字节的数据,写入s指向的字符数组中去
返回值:成功吸收返回s,失败返回NULL
吸收最多size个字节,size中包含了1个字节的结束符,所以,最多能吸收的有效字节数为:size-1个
使用fgets代替scanf
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
//从终端吸入size-1个有效数据
//使用fgets代替scanf
char *getstr(char *buf,int size){
char *res=fgets(buf,size,stdin);
int len=strlen(buf); //获取buf的长度,结束符在len下标上,所以回车就在len-1下标上
if(buf[len-1]=='\n')//这两句不写的话,就会有两个换行符号
buf[len-1]='\0';
return res;
}
int main(int argc, const char *argv[])
{
char buf[32]="0";
printf("%s\n",getstr(buf,32));
return 0;
}
2.fwrite、fread
fwrite和fread是数据流
文件流:任意数据,都会以字符串,然后再以字符串二进制的形式,写入文件中
数据流:所有数据,都以二进制的形式写入文件中
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能描述:将ptr指向的内存空间上,总共 size * nmemb 个字节的数据,写入stream指向的文件中
注意:将普通数据写入后,文件中呈现二进制状态
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能描述:读取stream指向的文件,总共读取nmemb个数据,每个数据大小为size,将这些数据写入 ptr指向的内存中
注意:fwrite、fread会强制读写 size*nmemb字节的数据,无论是否有那么多数据,成为模块化读写
int feof(FILE *stream);
功能描述:测试stream指向的文件,是否读写到了结束符,如果到了结束符,则返回非0(实际上是1),没到结束符返回0
注意:
1.因为在读取最后一个模块的时候,如果最后一个模块大小,没有我们设定的读取模块那么大的话,就会将文件结束符一起读取到模块里面去,然后通过feof判定文件读取结束,最后个模块就没有通过fwrite写出去
2.如果使用fwrite、fread配合feof去实现文件拷贝功能(一起其他类似的功能)的时候,如果模块大小没有计算精确的话,容易导致最后一个模块出问题
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
FILE* rfp = fopen(argv[1],"r");
FILE* wfp = fopen(argv[2],"w");
if(rfp==NULL || wfp==NULL){return 1;}
char buf[100] = {0};
while(1){
fread(buf,100,1,rfp);
if(feof(rfp)==1){break;}
fwrite(buf,100,1,wfp);
}
fclose(rfp);
fclose(wfp);
return 0;
}
3.文件光标偏移函数fseek
int fseek(FILE *stream, long offset, int whence);
功能:将stream指向的文件光标进行偏移,偏移量为offset个字节,offset为正表示从左往右偏,为负表示从右往左偏。whence决定偏移的起点
SEEK_SET:从文件开头开始偏移
SEEK_CUR:从文件当前光标位置开始偏移
SEEK_END:从文件末尾开始偏移
使用函数,修改图片
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
FILE* fp = fopen("rising_freedom.bmp","r");
fseek(fp,2,SEEK_SET);
int bmp_size = 0;
fread(&bmp_size,4,1,fp);
printf("文件大小为 %d 字节\n",bmp_size);
int w = 0,h = 0;
fseek(fp,18,SEEK_SET);
fread(&w,4,1,fp);
fread(&h,4,1,fp);
printf("图像尺寸为:%d * %d\n",w,h);
fclose(fp);
fp = fopen("rising_freedom.bmp","r+");
fseek(fp,54,SEEK_SET);
// bmp 图片默认像素格式是 bgr的
unsigned char pix[3] = {0,0,255};
for(int i=0;i<w;i++){
for(int j=0;j<h;j++){
fwrite(pix,3,1,fp);
}
}
fclose(fp);
return 0;
}
4.标准IO的缓存区
printf函数,缓存刷新机制
1:遇到回车自动刷新
2:缓存区满自动刷新
3:程序结束自动刷新
4:IO切换缓存刷新
5:描述符关闭缓存刷新
6:使用 fflush函数强制刷新特定缓存区
4.1缓存区的分类
1:行缓存:printf就是行缓存
2:全缓存:FILE*指向的非终端文件,都是全缓存,全缓存刷新机制就比行缓存缺一个回车
3:无缓存:没有缓存,stderr,标准错误流是无缓存
4.2scanf的缓存区
从键盘输入的所有数据,都会在缓存区,敲击回车,表示输入结束,这个回车也在缓存区
scanf吸收成功,就会将数据从缓存区中取走
吸收失败则会一直留在缓存区
所以,我们要通过语句 while(getchar()!='\n') 来清理所有缓存
int a=0;
scanf("%d",&a);
while(getchar()!='\n');//用于吸收垃圾字符
4.3缓存区大小的计算
FILE 结构体里面,有2个指针
_IO_buf_base:缓存区起始地址
_IO_buf_end:缓存区尾地址
所以,只要拿缓存区尾地址 - 缓存区起始地址 = 缓存区大小了
注意:如果想要计算缓存区的大小的话,我们必须先激活一下使用这个缓存区的文件流,也就是做一下IO操作
行缓存:1024字节
全缓存:4096字节
无缓存:0字节
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
printf("hello\n");//先激活缓存区的文件流
printf("行缓存大小为:%ld\n",stdout->_IO_buf_end - stdout->_IO_buf_base);
FILE* fp = fopen("./demo11.txt","w");
fputc('x',fp);
printf("全缓存大小为:%ld\n",fp->_IO_buf_end - fp->_IO_buf_base);
fclose(fp);
perror("123");
printf("无缓存大小为:%ld\n",stderr->_IO_buf_end - stderr->_IO_buf_base);
return 0;
}
二、作业
1.使用fwrite、fread将一张随意的bmp图片,修改成德国的国旗
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
FILE *fp=fopen("1.bmp","r");
int w=0,h=0;
fseek(fp,18,SEEK_SET);
fread(&w,4,1,fp);
fread(&h,4,1,fp);
printf("图像尺寸为:%d * %d\n",w,h);
fclose(fp);
fp=fopen("1.bmp","r+");
fseek(fp,54,SEEK_SET);
unsigned char pix1[3]={0,0,0};
unsigned char pix2[3]={0,0,255};
unsigned char pix3[3]={128,255,255};
int i=0,j=0;
for(i=0;i<w/3;i++){
for(j=0;j<h;j++){
fwrite(pix3,3,1,fp);
}
}
for(i=w/3;i<w/3*2;i++){
for(j=0;j<h;j++){
fwrite(pix2,3,1,fp);
}
}
for(i=w/3*2;i<w;i++){
for(j=0;j<h;j++){
fwrite(pix1,3,1,fp);
}
}
fclose(fp);
return 0;
}
2.使用提供的getch函数,编写一个专门用来输入密码的函数,要求输入密码的时候,显示 * 号,输入回车的时候,密码输入结束
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
int getch(){
int c=0;
struct termios org_opts, new_opts;
int res=0;
res=tcgetattr(STDIN_FILENO, &org_opts);
assert(res==0);
new_opts = org_opts;
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
c=getchar();
res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
assert(res==0);
return c;
}
int main(){
char a[32]="0";
int i;
for(i=0;a[i]!='\n';i++){
a[i]=getch();
if(a[i]=='\n')
break;
printf("*");
}
printf("%s",a);
putchar(10);
}