1.标准IO
1.1什么是FILE
在使用fopen打开文件的时候,会返回一个FILE*的指针(文件指针),FILE的本质是一个结构体,在这个结构体中记录所有的关于文件的信息,以后对文件的操作通过FILE*来完成。
/usr/include/
ctags -R //创建索引
vi -t FILE //查找FILE实现的位置
typedef struct _IO_FILE FILE;
struct _IO_FILE {
char* _IO_buf_base; //缓冲区的首地址
char* _IO_buf_end; //缓冲区的结束地址
...
};
在一个正在执行的程序中,已经创建了三个FILE的指针,如下:
stdin: 标准输入 (scanf)
stdout 标准输出 (printf)
stderr: 标准出错
1.2fopen的使用
man 后跟的数字的含义
2 系统调用(内核提供的函数)
3 库调用(程序库中的函数)
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:使用标准IO打开文件
参数:
@pathname: 路径/文件名 "./hello.txt"
@mode:打开文件的方式 "r" "r+"
r:以只读的方式打开文件,将文件中的光标定位在文件的开头
r+:以读写的方式打开文件,将文件中的光标定位在文件的开头
w:以只写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空,将光标定位在开头
w+:以读写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空,将光标定位在开头
a:以追加(结尾写)的方式打开文件,如果文件不存在就创建文件,将光标定位在文件的结尾
a+:以读和追加(结尾写)的方式打开文件,如果文件不存在就创建文件,如果读从开头读,写在文件的结尾写
返回值:成功返回文件指针,失败返回NULL,置位错误码
fopen的实例:
#include <stdio.h>
int main(int argc,const char * argv[])
{
//定义文件指针
FILE *fp;
//以只写的方式打开文件
// fp = fopen("./hello.txt","w");
// if(fp == NULL){
// printf("create file error\n");
// return -1;
// }
//以只读的方式打开文件,文件不存在会报错
// fp = fopen("./hello.txt","r");
// if(fp == NULL){
// printf("create file error\n");
// return -1;
// }
//以追加的方式打开文件,如果文件存在,不会清空(追加)
fp = fopen("./hello.txt","a");
if(fp == NULL){
printf("create file error\n");
return -1;
}
return 0;
}
1.3fclose的使用
int fclose(FILE *stream);
功能:关闭文件
参数:
@stream:文件指针
返回值:成功返回0,失败返回EOF(end of file),并置位错误码
#define EOF (-1)
fclose的使用:
#include <stdio.h>
int main(int argc,const char * argv[])
{
FILE *fp;
//以只写的方式打开文件
fp = fopen("./hello.txt","w");
if(fp == NULL){
printf("create file error\n");
return -1;
}
//关闭文件
if(fclose(fp)<0){
printf("close file error\n");
return -1;
}
if(fclose(stdin)<0){
printf("close file error\n");
return -1;
}
if(fclose(stdout)<0){
printf("close file error\n");
return -1;
}
if(fclose(stderr)<0){
printf("close file error\n");
return -1;
}
return 0;
}
1.4关于错误码的问题
在文件IO或标准IO相关接口被调用的时候,如果出错了,操作系统会给应用程序返回错误码。过程如下:
#include <string.h>
char *strerror(int errnum);
功能:根据错误码转换错误信息
参数:
@errnum:这个是错误码,包含
#include <errno.h>
extern int errno;
返回值:错误信息字符串
void perror(const char *s);
功能:打印错误码对应的信息
参数:
@s:用户附加的信息
返回值:无
1.4.1strerror函数的使用
#include <stdio.h>
#include <errno.h>
#include <string.h>
//extern int errno;
int main(int argc,const char * argv[])
{
//定义文件指针
FILE *fp;
//以只读的方式打开文件,文件不存在会报错
fp = fopen("./hello.txt","r");
if(fp == NULL){
printf("%s\n",strerror(errno));
return -1;
}
fclose(fp);
return 0;
}
1.4.2perror函数的使用
#include <stdio.h>
int main(int argc,const char * argv[])
{
//定义文件指针
FILE *fp;
//以只读的方式打开文件,文件不存在会报错
fp = fopen("./hello.txt","r");
if(fp == NULL){
perror("open hello.txt");
return -1;
}
fclose(fp);
return 0;
}
1.5fgetc/fputc函数使用
int fgetc(FILE *stream);
功能:从文件中读取一个字符
参数:
@stream:文件指针
返回值:成功返回字符的ascii,读取文件的结尾EOF,失败返回error
int fputc(int c, FILE *stream);
功能:向文件中写一个字符
参数:
@c:被写字符的ascii
@stream:文件指针
返回值:成功返回ascii,失败返回EOF
1.5.1fputc函数的使用
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp;
fp = fopen("./test.txt", "w");
if (fp == NULL)
{
perror("fopen error");
return -1;
}
fputc('h', fp);
fputc('e', fp);
fputc('l', fp);
fputc('l', fp);
fputc('o', fp);
fclose(fp);
return 0;
}
1.5.2fgetc函数的使用
#include <stdio.h>
int main(int argc,const char * argv[])
{
FILE *fp;
fp = fopen("./01fopen.c","r");
if(fp == NULL){
perror("fopen error");
return -1;
}
int i=0;
while(i++<30){
printf("%c",fgetc(fp));
}
puts("");
fclose(fp);
return 0;
}
1.5.3请使用fgetc统计文件的行数
#include <stdio.h>
int main(int argc,const char * argv[])
{
FILE * fp;
char ch;
int count=0;
//1.通过命令行输入可执行程序和文件名,否则就出错
if(argc != 2){
printf("input error,try agian\n");
printf("usage:./a.out filename\n");
return -1;
}
//2.以只读的方式打开文件
if((fp = fopen(argv[1],"r"))==NULL){
perror("fopen error");
return -1;
}
//3.只要没有到文件结尾,循环就一直执行(EOF)
while((ch = fgetc(fp)) != EOF){
if(ch == '\n'){ //如果文件中有'\n'就让行号+1
count++;
}
}
printf("file line number = %d\n",count+1);
//关闭文件
fclose(fp);
return 0;
}
1.5.4请使用fgets/fputc拷贝一个文件
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *sfp, *dfp;
char ch;
if (argc != 3)
{
printf("input error,try agian\n");
printf("usage:./a.out srcfile destfile\n");
return -1;
}
if ((sfp = fopen(argv[1], "r")) == NULL)
{
perror("fopen src file error");
return -1;
}
if ((dfp = fopen(argv[2], "w")) == NULL)
{
perror("fopen dest file error");
return -1;
}
while ((ch = fgetc(sfp)) != EOF)
{
fputc(ch,dfp);
}
fclose(sfp);
fclose(dfp);
return 0;
}
1.6fgets/fputs函数使用
char *fgets(char *s, int size, FILE *stream);
功能:从文件指针中读取一个字符串
注:遇到换行或者EOF的停止读取,换行符也会被读取到s的缓冲区中,并且在换行符之后存储一个'\0'结束符
参数:
@s:用来存储读取到字符的首地址
@size:读取的大小(最多读取size-1)
@stream:文件指针
返回值:成功返回s,失败返回NULL
int fputs(const char *s, FILE *stream);
功能:将s字符串中的内容写入到文件中
参数:
@s:首地址
@stream:文件指针
返回值:成功返回写入的字符的个数,失败返回EOF
1.6.1使用fgets从stdin中读取字符串
#include <stdio.h>
#include <string.h>
int main(int argc,const char * argv[])
{
char buffer[1024];
printf("input string > ");
fgets(buffer,sizeof(buffer),stdin);
//将读取到的换行符设置为'\0'
//hello\n
//buffer=hello'\n''\0'
//strlen(buffer)=6
//buffer[5]='\0'
buffer[strlen(buffer)-1]='\0';
printf("%s\n",buffer);
return 0;
}
1.6.2fputs函数的使用
#include <stdio.h>
int main(int argc,const char * argv[])
{
FILE*fp;
//向标准输出中写入字符串
fputs("helloworld",stdout);
//向标准出错中写入字符串
fputs("helloworld",stderr);
if ((fp = fopen("hello.txt", "w")) == NULL)
{
perror("fopen file error");
return -1;
}
fputs("i love china",fp);
fclose(fp);
return 0;
}
1.6.3使用fgets统计文件的行号
#include <stdio.h>
#include <string.h>
int main(int argc,const char * argv[])
{
FILE *fp;
char s[10];
int line=0;
if(argc != 2){
fputs("input error,try again\n",stderr);
fputs("usage:./a.out file\n",stderr);
}
if((fp = fopen(argv[1],"r"))== NULL){
perror("fopen error");
return -1;
}
while(fgets(s,sizeof(s),fp)!=NULL){
if(strlen(s)==sizeof(s)-1){
if(s[sizeof(s)-2]!='\n'){
continue;
}
}
line++;
}
printf("line = %d\n",line);
fclose(fp);
return 0;
}
1.6.4使用fgets和fputs实现文件的拷贝
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *sfp, *dfp;
char s[40];
if (argc != 3)
{
printf("input error,try agian\n");
printf("usage:./a.out srcfile destfile\n");
return -1;
}
if ((sfp = fopen(argv[1], "r")) == NULL)
{
perror("fopen src file error");
return -1;
}
if ((dfp = fopen(argv[2], "w")) == NULL)
{
perror("fopen dest file error");
return -1;
}
while(fgets(s,sizeof(s),sfp)!=NULL){
fputs(s,dfp);
}
fclose(sfp);
fclose(dfp);
return 0;
}
2.格式化控制相关的函数
2.1sprintf函数使用(将字符串格式化到数组中)
int sprintf(char *str, const char *format, ...);
功能:向str中进行字符串的格式化
参数:
@str:首地址
@format:控制格式
返回值:成功格式化字符的个数,失败返回负数
char s[50];
sprintf(s,"%4d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
把双引号中的字符串存放在s的数组中
s="2022-4-26 17:30:20"
fputs(s,fp); //将s中的字符串写入到文件中
注:当使用sprintf往数组中写入字符的时候,如果出现越界,虽然编译的时候会提示警告,但是在程序运行的时候有多少字符就格式化多少字符,这个内存越界的错误
2.2snprintf函数使用(将字符串格式化到数组中)
int snprintf(char *str, size_t size, const char *format, ...);
功能:将字符串格式化到str的数组中
参数:
@str:存放格式化后的字符串的内存首地址
@size:大小(字符size-1)
@format:格式化的控制格式
返回值:成功返回字符的个数,失败返回负数
#include <stdio.h>
int main(int argc,const char * argv[])
{
char s[10];
snprintf(s,sizeof(s),"%s","helloworld1234");
printf("s = %s\n",s);
//上输入的字符个数大于s的大小的时候,最多格式化sizeof(s)-1个字符,在s最后一个字符的
//位置补上'\0'
return 0;
}
2.3fprintf的使用(将格式化后的字符串向对应的文件指针中输出)
int fprintf(FILE *stream, const char *format, ...);
功能:将格式化后的字符串向对应的文件指针中输出
参数:
@stream:文件指针
@format:控制格式
返回值:成功返回>0,失败返回<0
#include <stdio.h>
#include <time.h>
#include <string.h>
int getLineNum(FILE*fp)
{
char s[10];
int line=0;
while (fgets(s, sizeof(s), fp) != NULL)
{
if (strlen(s) == sizeof(s) - 1)
{
if (s[sizeof(s) - 2] != '\n')
{
continue;
}
}
line++;
}
return line;
}
int main(int argc, const char *argv[])
{
FILE *fp;
time_t time_new, time_old;
struct tm *tm;
char s[50];
int line = 0;
if ((fp = fopen("tim.txt", "a+")) == NULL)
{
perror("fopen src file error");
return -1;
}
line=getLineNum(fp);
printf("line = %d\n",line);
while (1)
{
time(&time_new);
tm = localtime(&time_new);
if (tm == NULL)
{
perror("change time error");
return -1;
}
if (time_new != time_old)
{
time_old = time_new;
fprintf(fp,"%3d.%4d-%02d-%02d %02d:%02d:%02d\n",
++line, tm->tm_year + 1900,tm->tm_mon + 1,
tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
fflush(fp);
}
}
fclose(fp);
return 0;
}
3.fread/fwrite函数的使用
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取数据到ptr中
参数:
@ptr:保存读取到数据的首地址
@size:每一项的大小
@nmemb:项的个数
@stream:文件指针
返回值:成功返回读取到的项目的个数,
如果是失败或者读取到的文件的结尾返回值是要小于nnemb或者0
错误还是读取到文件的结尾需要通过feof(fp)或者ferror(fp)来判断
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:将ptr中的数据写入到文件中
参数:
@ptr:数据的首地址
@size:每一项的大小
@nmemb:项的个数
@stream:文件指针
返回值:成功返回写的项目的个数,
如果是失败返回值是要小于nnemb或者0
3.1fwrite/fread函数的使用
3.1.1fwrite/fread整数读写
#include <stdio.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
int main(int argc,const char * argv[])
{
FILE *fp;
int num=1234;
if((fp = fopen("test.txt","w"))==NULL)
PRINT_ERR("fopen error");
fwrite(&num,4,1,fp);
//写入的整数,虽然在文本编辑器中看到的是乱码,但是可以通过fread正确读取到
fclose(fp);
return 0;
}
#include <stdio.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
int main(int argc,const char * argv[])
{
FILE *fp;
int num;
if((fp = fopen("test.txt","r"))==NULL)
PRINT_ERR("fopen error");
fread(&num,4,1,fp);
printf("read num = %d\n",num);
fclose(fp);
return 0;
}
3.1.2fwrite/fread字符串读写
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
int main(int argc,const char * argv[])
{
FILE *fp;
char str[] = "this is test for fwrite string";
if((fp = fopen("test.txt","w"))==NULL)
PRINT_ERR("fopen error");
fwrite(str,1,strlen(str),fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
int main(int argc,const char * argv[])
{
FILE *fp;
char str[50] ;
if((fp = fopen("test.txt","r"))==NULL)
PRINT_ERR("fopen error");
fread(str,1,sizeof(str),fp);
printf("read str = %s\n",str);
fclose(fp);
return 0;
}
3.1.3fwrite/fread结构体读写
typedef struct{
char name[20];
char sex;
int score;
}stu_t;
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
typedef struct{
char name[20];
char sex;
int score;
}stu_t;
int main(int argc,const char * argv[])
{
FILE *fp;
stu_t stu = {
"zhangsan",
'm',
99,
};
if((fp = fopen("test.txt","w"))==NULL)
PRINT_ERR("fopen error");
fwrite(&stu,sizeof(stu),1,fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
typedef struct{
char name[20];
char sex;
int score;
}stu_t;
int main(int argc,const char * argv[])
{
FILE *fp;
stu_t stu;
if((fp = fopen("test.txt","r"))==NULL)
PRINT_ERR("fopen error");
fread(&stu,sizeof(stu),1,fp);
printf("name = %s,sex = %c,score = %d\n",
stu.name,stu.sex,stu.score);
fclose(fp);
return 0;
}
3.1.4feof/ferror函数的使用
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0)
int main(int argc,const char * argv[])
{
FILE *fp;
char buf[10] = {0};
if((fp = fopen("test.txt","r"))==NULL)
PRINT_ERR("fopen error");
while(1){
//memset将buf中的内容全部写为0,大小是是sizeof(buf)
memset(buf,0,sizeof(buf));
fread(buf,1,sizeof(buf),fp);
printf("%s",buf);
if(feof(fp)){
fprintf(stderr,"读取到文件的结尾了\n");
break;
}else if(ferror(fp)){
fprintf(stderr,"读取到文件出错了\n");
break;
}
}
fclose(fp);
return 0;
}
3.1.5使用fread/fwrite实现拷贝文件的功能
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char *argv[])
{
FILE *sfp, *dfp;
char buf[10];
int ret;
if(argc != 3){
fprintf(stderr,"input error, tryagain\n");
fprintf(stderr,"usage:./a.out srcfile destfile");
return -1;
}
if ((sfp = fopen(argv[1], "r")) == NULL)
PRINT_ERR("fopen error");
if ((dfp = fopen(argv[2], "w")) == NULL)
PRINT_ERR("fopen error");
//如果没有到文件的结尾并且没有出错,就执行循环
//如果读取到结尾或者出错,就退出循环
while (!(feof(sfp) || ferror(sfp)))
{
// memset将buf中的内容全部写为0,大小是是sizeof(buf)
//memset(buf, 0, sizeof(buf));
//上述的memset可以不写,原因是通过ret来决定写入字符的个数
//即使buf中没有'\0'的结束符,也是可以正常执行的
ret = fread(buf, 1, sizeof(buf), sfp);
fwrite(buf,1,ret,dfp);
}
fclose(sfp);
fclose(dfp);
return 0;
}
4.关于光标位置的相关函数(fseek/ftell/rewind)
int fseek(FILE *stream, long offset, int whence);
功能:修改光标的位置
参数:
@stream:文件指针
@offset:偏移
>0 向后偏移
=0 不偏移
<0 向前偏移
@whence:
SEEK_SET,开头
SEEK_CUR,当前位置
SEEK_END,结尾
返回值:成功返回0,失败返回-1置位错误码
eg:
fseek(fp,0,SEEK_SET);将光标定位在文件的开头
fseek(fp,10,SEEK_SET);从文件的开头向后偏移10个字节
fseek(fp,10,SEEK_CUR);从光标当前位置向后偏移10个字节
fseek(fp,-10,SEEK_END);从文件结尾向前偏移10个字节
fseek(fp,0,SEEK_END);将光标定位在文件结尾
long ftell(FILE *stream)
功能:获取光标当前的位置
参数:
@stream:文件指针
返回值:成功返回光标的位置,失败返回-1,置位错误码
eg:
fseek(fp,0,SEEK_END);
ftell(fp); ===>功能统计文件所占用的字符的个数(大小)
void rewind(FILE *stream);
功能:恢复光标的位置到文件的开头
参数:
@stream:文件指针
返回值:无
eg:
rewind(fp) == fseek(fp,0,SEEK_SET) 等价
#include <stdio.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
FILE * fp;
char s[50];
if ((fp = fopen("./test.txt", "r")) == NULL)
PRINT_ERR("fopen error");
fseek(fp,6,SEEK_SET);
fgets(s,sizeof(s),fp);
printf("s = %s\n",s);
fclose(fp);
return 0;
}
思考:如果通过追加的方式打开文件,修改光标的位置是否能影响读写?
答:如果是以"a"打开文件,修改不了光标的位置,在想文件中写数据的时候总是在结尾写
如果是以"a+"打开文件,修改光标影响不了写的光标的位置,但是可以影响读的光标的位置
#include <stdio.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
FILE * fp;
char s[50];
if ((fp = fopen("./test.txt", "a")) == NULL)
PRINT_ERR("fopen error");
fseek(fp,0,SEEK_SET); //将追加是结尾的光标修改到文件的开头,在向文件中写
//数据的时候依然是在结尾写,影响不了写的光标的位置
fprintf(fp,"i am test fseek func append");
fclose(fp);
return 0;
}
#include <stdio.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
FILE * fp;
char s[50];
if ((fp = fopen("./test.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
fseek(fp,6,SEEK_SET); //可以影响读的光标的位置
fgets(s,sizeof(s),fp);
printf("s = %s\n",s);
fclose(fp);
return 0;
}
作业1
将当前的时间写入到tim.txt的文件中,如果ctrl+c退出之后,在再次执行支持断点续写
1.2022-04-26 19:10:20
2.2022-04-26 19:10:21
3.2022-04-26 19:10:22
//按下ctrl+c停止,再次执行程序
4.2022-04-26 20:00:00
5.2022-04-26 20:00:01
获取系统时间的函数
#include <time.h>
time_t time(time_t *tloc);
功能:获取系统当前时间的秒钟数
参数:
@tloc:如果是NULL,通过返回值返回秒钟数
如果不是NULL,通过参数得到秒钟数
返回值:成功返回time_t的地址,失败返回(time_t*)-1,并置位错误码
struct tm *localtime(const time_t *timep);
功能:将秒钟转化为年月日时分秒的格式
参数:
@timep:秒钟数
返回值:成功返回tm的结构体指针,失败返回NULL,置位错误码
struct tm {
int tm_sec; //秒钟
int tm_min; //分钟
int tm_hour; //小时
int tm_mday; //天
int tm_mon; //月+1
int tm_year; //年+1900
int tm_wday; /* 周几 (0-6, Sunday = 0) */
int tm_yday; //一年内的第几天
int tm_isdst; //夏令时,已经被废弃了
};
#include <stdio.h>
#include <time.h>
int main(int argc,const char * argv[])
{
time_t time_v;
struct tm *tm;
time(&time_v);
tm = localtime(&time_v);
if(tm == NULL){
perror("change time error");
return -1;
}
printf("%4d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
return 0;
}
sprintf函数的使用
int sprintf(char *str, const char *format, ...);
功能:向str中进行字符串的格式化
参数:
@str:首地址
@format:控制格式
返回值:成功格式化字符的个数,失败返回负数
char s[50];
sprintf(s,"%4d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
把双引号中的字符串存放在s的数组中
s="2022-4-26 17:30:20"
fputs(s,fp); //将s中的字符串写入到文件中
#include <stdio.h>
#include <time.h>
int main(int argc,const char * argv[])
{
FILE *fp;
time_t time_v;
struct tm *tm;
char s[50];
if ((fp = fopen("hello.txt", "a")) == NULL)
{
perror("fopen src file error");
return -1;
}
time(&time_v);
tm = localtime(&time_v);
if(tm == NULL){
perror("change time error");
return -1;
}
sprintf(s,"%4d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
fputs(s,fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#include <time.h>
int main(int argc,const char * argv[])
{
FILE *fp;
time_t time_v;
struct tm *tm;
char s[50];
if ((fp = fopen("hello.txt", "a")) == NULL)
{
perror("fopen src file error");
return -1;
}
time(&time_v);
tm = localtime(&time_v);
if(tm == NULL){
perror("change time error");
return -1;
}
sprintf(s,"%4d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
fputs(s,fp);
fclose(fp);
return 0;
}
作业:向文件中打印系统时间
#include <stdio.h>
#include <time.h>
#include <string.h>
int getLineNum(FILE*fp)
{
char s[10];
int line=0;
while (fgets(s, sizeof(s), fp) != NULL)
{
if (strlen(s) == sizeof(s) - 1)
{
if (s[sizeof(s) - 2] != '\n')
{
continue;
}
}
line++;
}
return line;
}
int main(int argc, const char *argv[])
{
FILE *fp;
time_t time_new, time_old;
struct tm *tm;
char s[50];
int line = 0;
if ((fp = fopen("tim.txt", "a+")) == NULL)
{
perror("fopen src file error");
return -1;
}
line=getLineNum(fp);
printf("line = %d\n",line);
while (1)
{
time(&time_new);
tm = localtime(&time_new);
if (tm == NULL)
{
perror("change time error");
return -1;
}
if (time_new != time_old)
{
time_old = time_new;
sprintf(s, "%3d.%4d-%02d-%02d %02d:%02d:%02d\n",
++line, tm->tm_year + 1900,tm->tm_mon + 1,
tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
fputs(s, fp);
fflush(fp);
}
}
fclose(fp);
return 0;
}
作业2
使用标准IO对文件打马赛克
使用标准IO对图片操作
自己编写一个head.h的头文件,将它放在/usr/include/目录下,以后包含这个头文件,就可以使用打印的宏
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) do{ \
perror(msg);\
return -1;\
}while(0)
#endif
bmp的图片格式详解
参看这篇文章:https://www.ivu4e.com/blog/image/2019-09-04/183.html
在ubuntu终端上可以通过eog xxx.bmp打开一张图片
bmp文件操作实例
#include <head.h>
int main(int argc, const char *argv[])
{
FILE *fp;
int img_size;
unsigned char color[3] = {0x0, //蓝
0x0,//绿
0xff}; //红
if (argc != 2)
{
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out picture.bmp\n");
return -1;
}
if ((fp = fopen(argv[1], "r+")) == NULL)
PRINT_ERR("fopen error");
fseek(fp, 2, SEEK_SET);
fread(&img_size, 4, 1, fp);
printf("img_size = %d\n", img_size);
fseek(fp, 54, SEEK_SET); //跳过图片的头
for (int j = 0; j < 100; j++) //写多少行
{
for (int i = 0; i < 2560; i++) //每一行内的像素点
{
fwrite(color, 3, 1, fp); //每一个点占3个字节
}
}
fclose(fp);
return 0;
}
给图片打马赛克
#include <head.h>
#include <stdlib.h>
typedef struct{
unsigned int img_size; //图片的大小
unsigned int img_width;//图片的宽
unsigned int img_high; //图片的高
unsigned short img_bitcount; //一个像素点占用的bit(24bit)
}image_info_t;
typedef struct{
unsigned char b;
unsigned char g;
unsigned char r;
}point_t;
void show_image_info(image_info_t *info)
{
printf("size = %d,width = %d,high = %d,bitcount = %d\n",
info->img_size,info->img_width,info->img_high,info->img_bitcount);
}
void get_image_info(FILE*fp,image_info_t *info)
{
fseek(fp,2,SEEK_SET);
fread(&info->img_size,1,4,fp); //图片的大小
fseek(fp,18,SEEK_SET);
fread(&info->img_width,1,4,fp); //图片的宽
fread(&info->img_high,1,4,fp); //读取图片的高
fseek(fp,2,SEEK_CUR);
fread(&info->img_bitcount,1,2,fp); //像素点占用的bit
}
void copy_image_file(FILE*sfp,FILE*dfp)
{
int ret;
char buf[1024] = {0};
while(!(feof(sfp)||ferror(sfp))){
ret = fread(buf,1,sizeof(buf),sfp);
fwrite(buf,1,ret,dfp);
}
return;
}
void set_image_mosaic(FILE *fp,image_info_t *info,int x,int y)
{
int i,j,k,w;
point_t color = {0,0,0xff};
char *buffer = (char *)malloc((info->img_width)*(info->img_high)*3);
//point_t arr[612][440];
//1.将图像读取回来
fseek(fp,54,SEEK_SET);
fread(buffer,1,(info->img_size-54),fp);
//2.修改
//i:整体的高/10
//j:整体的宽除以10
//k:块的高
//w:块的宽
for(i=0;i<info->img_high/y;i++){
for(j=0;j<info->img_width/x;j++){
//读取小方块中最左上角的像素点
color = *(point_t *)(buffer+(j*3*x)+(i*y*info->img_width*3));
for(k=0;k<y;k++){
for(w=0;w<x;w++){
*(point_t*)(buffer+w*3+(k*info->img_width*3)+
(j*3*x)+(i*y*info->img_width*3)) = color;
}
}
}
}
//3.重新将图像写回去
fseek(fp,54,SEEK_SET);
fwrite(buffer,1,(info->img_size-54),fp);
}
int main(int argc, char const *argv[])
{
FILE *sfp,*dfp;
int size;
image_info_t info;
char new_name[20] = {0};
if(argc != 2){
fprintf(stderr,"input error,try again\n");
fprintf(stderr,"usage:./a.out xxxx.bmp\n");
return -1;
}
//1.打开文件并拷贝文件 milaoshu.bmp
if((sfp = fopen(argv[1],"r"))==NULL)
PRINT_ERR("open error");
//构造一个新图片的字符串 new_milaoshu.bmp
snprintf(new_name,sizeof(new_name),"new_%s",argv[1]);
//打开新图片,如果不存在就创建,如果存在就清空
if((dfp = fopen(new_name,"w+"))==NULL)
PRINT_ERR("open error");
//图片的拷贝,将milaoshu.bmp-->new_milaoshu.bmp
copy_image_file(sfp,dfp);
//2.获取图片前54个字节中有用的信息
get_image_info(dfp,&info);
show_image_info(&info);
//3.尝试打马赛克
//10,10:代表的是打马赛克每个小方块的大小
//10*3= 30
//10 = 10行
set_image_mosaic(dfp,&info,10,10);
//4.关闭源文件和目标文件
fclose(sfp);
fclose(dfp);
return 0;
}
作业3:
使用文件IO实现,将当前的时间写入到tim.txt的文件中
2022-04-26 19:10:20
2022-04-26 19:10:21
2022-04-26 19:10:22
使用文件IO写入时间
#include <head.h>
#include <time.h>
int main(int argc, const char *argv[])
{
int fd;
time_t time_new, time_old;
struct tm *tm;
char s[50] = {0};
if ((fd = open("tim.txt",O_RDWR|O_APPEND|O_CREAT,0664)) == -1)
PRINT_ERR("open error");
while (1)
{
time(&time_new);
tm = localtime(&time_new);
if (tm == NULL)
{
perror("change time error");
return -1;
}
if (time_new != time_old)
{
time_old = time_new;
sprintf(s, "%4d-%02d-%02d %02d:%02d:%02d\n",
tm->tm_year + 1900,tm->tm_mon + 1,
tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
write(fd,s,strlen(s));
}
}
close(fd);
return 0;
}