Day1 标准io介绍及缓冲区
“一切皆文件”
文件基础:一组相关数据的集合
文件类型:
常规文件 r
目录文件 d
字符设备文件 c
块设备文件 b (u盘,磁盘)
管道文件 p
套接字文件 s
符号链接文件 l
标准io通过缓冲机制减少系统调用,实现更高效的效率
标准io------流
FILE
标准io用一个结构体类型来存放打开文件的相关信息
标准io所有的操作都是围绕着流来进行的
文本流/二进制流
标准io—流的缓冲类型
全缓冲
当流的缓冲区无数据或者无空间时才执行io操作
行缓冲
当在输入和输出遇到换行符时进行io操作
流和一个终端互联,典型的行缓冲
无缓冲
数据直接写入文件,流不进行缓冲
标准io—stdin,stdout,stderr
标准输入流 0 STDIN_FILENO
标准输出流 1 STDOUT_FILENO
标准错误流 2 STDERR_FILENO
Day2 文件的打开,关闭及代码实现
文件的打开
打开就是占用资源
文件的关闭
关闭就是释放资源
标准io—打开文件
下列函数可用于打开一个标准io流:
FILE *fopen(const char *path,const char *mode);
pash路径文件,mode模式
成功返回流指针;出错返回NULL;
文件的打开实现
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(){
FILE *fp;
int i;
fp = fopen("test.txt", "r");
if (fp == NULL)
{
//printf("Error!\n");
perror("fopen:");
printf("fopen:%s\n",strerror(errno));
}else{
printf("File opened!\n");
}
}
编译错误
‘errno’ undeclared (first use in this function)
printf(“fopen:%s\n”,strerror(errno));
表示error没有被定义
解决办法:如果是系统变量用include头文件,如果是自己的,手动定义
perror函数只需要一个stdio.h头文件,
strerror 需要string.h,和error.h两个头文件
文件的关闭实现
int fclose(FILE * stream);
flose()调用成功返回0,错误返回-1.
流关闭时自动刷新缓冲区并释放,
当一个程序终止,所有打开的流都会关闭,
流一旦关闭之后就不能执行任何操作,
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(){
FILE *fp;
int fpret;
int i;
fp = fopen("test.txt", "r");
if (fp == NULL)
{
//printf("Error!\n");
perror("fopen:");
printf("fopen:%s\n",strerror(errno));
}else{
printf("File opened!\n");
fpret = fclose(fp);
if (fpret == 0)
{
printf("file close sucess"); /* code */
}else{
perror("fclose:");
}
}
fclose()函数的入参stream必须保证为非空,否则出现段错误。
Day3 标准io的读写
按字符输入/输出
fgetc()/fputc() 一次读/写一个字符
#include<stdio.h>
int main(){
FILE *fp;
int rec;
fp=fopen("file.txt","r");
if (fp == NULL)
{
perror("Error while opening the file.\n");
return 0;
}
rec = fgetc(fp);
printf("get char=%c\n", rec);
}
打开文件后读取,是从文件开头开始读取,然后读取下一个,这里时FILE本身定义封装的时候的时候有一个隐藏的数据结构,读完一个后读写指针会后移。
调用getchar会阻塞 等待键盘输入,
bad file descriptor 很可能是文件打开模式错误(只读模式去写入,只写模式去读)
行输入(读取行)
char *gets(char *s);一般不使用,容易造成缓冲区溢出
char *fgets(char *s, int size,FILE *stream); 总是包含’\0’ 实际数据时size-1个数据
注意事项:
gets函数以及该被淘汰 不用了
fgets函数第二个参数的大小,输入的数据超过size只保存size-1,最后添加\0,输入的数据小于size-1 后面会自动添加换行符
行输出(写整行)
int puts(const char *s);
int fputs(const char *s,FILE *stream);
puts将缓冲区s中的字符串输出到stdout追加换行符
fputs不追加
一般两个都使用 printf包含以上两个函数所有的功能。
#include<stdio.h>
int main(){
FILE *fp;
char *ret;
int i;
char buff[100];
fp=fopen("file.txt","r+");
if(fp==NULL){
perror("fopen");
return 0;
}
/* ret = fgets(buff,5,fp);
if (ret==NULL)
{
perror("fgets");
fclose(fp);
return 0;
}
printf("buff=%s\n",buff);
}
*/
i = fputs("hello world",fp);
if(i==EOF){
perror("fputs");
}
printf("jhhhhhhhhh");
fclose(fp);
}
Day4 二进制方式
按指定对象输入
按指定对象输出
二进制读写
文本文件和二进制文件的区别:
存储格式不同:文本文件只能存储文本,
计算机内码概念:
二进制方式的读写:
size_t fread(void *ptr,size_t size,size_t n,FILE *fp);
void *ptr 读取的内容
size_t size 读取的块大小
size_t n 读取的个数
FILE *fp 读取的文件指针
size_t fwrie(const void *ptr,size_t size,size_t n,FILE *fp);
成功返回读写的对象个数;出错时返回-1;
即可以读写文本文件,也可以读写数据文件(二进制文件)
ret = fwrite(&stu,sizeof(stu),1,fp);
if (ret == -1)
{
perror("fwrite");
goto end;
}else{
printf("write struct student success\n");
}
ret = fread(&stu2,sizeof(stu),1,fp);
if (ret == -1)
{
perror("fread");
goto end;
}
printf("name:%s\n",stu2.name);
printf("age:%d\n",stu2.age);
printf("sex:%s\n",stu2.sex);
这u但代码执行结束会发现读出来是乱码 这是因为第一次写入的时候指针已经指向了末尾,读自然也是从最后一位读,后面没有数据,自然就是乱码,这里一般采用关闭重新打开文件的方式,或者后续会学到的文件的指针移动
Day5 流的刷新定位
流的刷新
int fflush(FILE *fp);
成功返回0,出错返回-1.
将流缓冲区中的数据写入实际文件,
linux下只能刷新输出缓冲区,输入缓冲区丢弃。
#include<stdio.h>
#include<unistd.h>
int main(){
//printf("abcdefg");
//fflush(stdout);
FILE *fp;
fp=fopen("2.txt","w");
if(fp==NULL){
perror("fopen");
return 0;
}
fwrite("abcdef",7,1,fp);
fflush(fp);
while(1){
sleep(1);
}
}
原本存在循环,所以无法将缓冲区的数据写入到实际文件当中,所以加上fflush函数刷新流 在就可以写入实际的文件当中
流的定位
long ftell(FILE *stream);
long fseek(FILE *stream,long offset,int whence);
void rewind(FILE *stream);
ftell()成功返回流的当前读写位置,出错时返回-1
fseek()定位一个流,成功时返回0,出错时返回-1
whence参数;SEEK_SET/SEEK_CUR/SEEK_END
SEEK_SET从据文件开头offset位移量为新的读写位置。
用a模式打开 fseek无效
offset参数:位移量,可正可负,
rewind()将流定位到文件开始位置
rewind(fp)相当于fseek(fp,0,SEEK_SET)
读写流时,当前读写位置自动后移
这三个函数只适用于2g以下的文件。
FILE *fp = fopen(“test.txt”,“r+”);
fseek(fp,0,SEEK_END);
fputc(“t”,fp);
#include<stdio.h>
int main(){
FILE *fp;
fp=fopen("2.txt","w");
if(fp==NULL){
perror("fopen");
return 0;
}
fwrite("abcdef",6,1,fp);
fseek(fp,-2,SEEK_CUR);
fwrite("vvv",3,1,fp);
}
输出结果为abcdvvv。
Day6 格式化输入输出
格式化输出
sprintf
字符串
int sprintf(char *s,const char *fmt,…);
#include<stdio.h>
int main(){
char buf[100]={0};
int year = 2023;
int month = 8;
int day = 1;
sprintf(buf,"%d-%d-%d",year,month,day);
printf("%s",buf);
}
fprintf
文件
int fprintf(FILE *stream,从上图char * fmt,…);
成功时返回输出的字符个数,出错时返回EOF
使用起来很方便,强烈推荐
#include<stdio.h>
int main(){
FILE *fp;
int year=2023;
int month=8;
int day=1;
fp = fopen("3.txt", "w");
if (fp==NULL)
{
perror("fopen");
return 0;
}
fprintf(fp, "%d-%d-%d",year,month,day);
fclose(fp);
}
格式化输入
int fscanf(FILE *stream, const char *format, …);
int sscanf(const char *str, const char *format, …);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int day, year;
char weekday[20], month[20], dtm[100];
strcpy( dtm, "Monday April 10 2023" );
sscanf( dtm, "%s %s %d %d", weekday, month, &day, &year );
printf("%s %d, %d = %s\n", month, day, year, weekday );
return(0);
}
Day7 标准IO练习
1、每隔1秒向文件1.txt写入当前系统时间,行号递增
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
FILE *fp;
time_t ctime;
int linecount;
struct tm *ctimestr;
char buf[22];
fp = fopen("test.txt","a+");
if(fp == NULL){
perror("fopen");
return 0;
}
while(fgets(buf,22,fp) != NULL){
if(buf[strlen(buf) - 1] == '\n'){
linecount++;
}
}
while(1){
ctime = time(NULL);
// printf("ctime = %d\n",(int)ctime);
ctimestr = localtime(&ctime);
printf("%04d-%02d-%02d %02d:%02d:%02d\n",ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
fprintf(fp,"%d, %04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
fflush(fp);
linecount++;
sleep(1);
}
fclose(fp);
return 0;
}
Day8:文件IO(概念、打开、读、写、关闭)
1、使用文件IO实现“每隔1秒向文件1.txt写入当前系统时间,行号递增”
#include<stdio.h>
#include<time.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
int fd;
time_t ctime;
struct tm *ctimestr;
int ret;
int linecount = 0;
char buf2[100];
fd = open("1.txt",O_RDWR|O_CREAT|O_APPEND, 0666);
if(fd==-1){
printf("open failed\n");
return 0;
}
while(read(fd,buf2,strlen(buf2)) != 0){
if (buf2[strlen(buf2)-1] == '\n'){
linecount++;
}
}
while(1){
ctime = time(NULL);
ctimestr = localtime(&ctime);
printf("%d, %04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
sprintf(buf2,"%d, %04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
ret = write(fd,buf2,strlen(buf2));
if(ret<0){
perror("write");
close(fd);
}
fflush(fd);
linecount++;
sleep(1);
}
close(fd);
}
Day9:文件IO(概念、打开、读、写、关闭)
1、遍历一个文件夹下所有文件,并打印文件大小和日期
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
int main(int argc, const char *argv[])
{
DIR * dir;
struct dirent *dent;
struct stat buf;
dir = opendir("/home/linux/lv3/linklist1/");
if(dir == NULL){
perror("opendir");
return 0;
}
while((dent = readdir(dir)) != NULL){
stat(dent->d_name,&buf);
struct tm *t = localtime(&buf.st_ctime);
printf("create time>%04d-%02d-%02d %02d:%02d:%02d ",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
printf("name>%-16s size>%d\n",dent->d_name,(int)buf.st_size);
}
closedir(dir);
return 0;
}
Day10:静态库和动态库的使用
1、静态库和动态库都有什么特点?他们的区别是什么?
静态库:
特点:
一般扩展名为(.a或.lib),这类的函数库通常扩展名为libxxx.a或xxx.lib 。
静态库在编译时被链接到目标文件中,程序运行时不需要再额外加载库文件。
可以在不同平台上编译相同的源代码,而无需考虑库的兼容性问题。
程序的执行速度比动态库快,因为所有的库函数都在编译时被链接到可执行文件中。
静态库的使用非常简单,只需要将库文件和头文件包含在项目中即可。
区别:
静态库的体积比较大,因为它包含了所有需要的函数和代码。
如果多个程序都使用同一个静态库,那么每个程序都会有一份完整的库文件,会占用较多的磁盘空间。
如果静态库更新了,需要重新编译程序才能使用新的库函数。
动态库:
特点:
动态函数库的扩展名一般为(.so或.dll),这类函数库通常名为libxxx.so或xxx.dll 。
动态库在程序运行时才会被加载,可以在程序运行时动态地链接和卸载库文件。
多个程序可以共享同一个动态库,节省磁盘空间。
动态库的更新只需要替换库文件,程序不需要重新编译。
动态库可以提供插件功能,允许程序在运行时加载外部库。
区别:
动态库的执行速度比静态库慢,因为需要在程序运行时加载和链接库文件。
动态库的使用相对复杂,需要在程序中显式地加载和卸载库文件。
动态库的兼容性问题比较复杂,不同操作系统和编译器可能需要不同版本的库文件。