一、关于重新定位问题
1、 重新定位文件流指针 -> fseek() -> man 3 fseek
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
参数:
- stream:文件指针
- offset:需要偏移的字节数
- whence:SEEK_SET -> 相对于开头;SEEK_CUR -> 相对于当前位置;SEEK_END -> 相对于文件末尾
返回值:
- 成功:0
- 失败:-1
获取文件大小:
int getFileSize(FILE*fp)
{
//1、先保存当前文件的位置
int curPos = ftell(fp);
//2、将文件偏移到末尾
fseek(fp,0,SEEK_END);
//3、获取文件大小
int fileSize = ftell(fp);
//4、恢复原来的位置
fseek(fp,curPos,SEEK_SET);
//5、返回文件的大小
return fileSize;
}
2、重置文件指针。 -> rewind() -> man 3 rewind
作用:rewind()用来把文件流的读写位置移至文件开头. 参数 stream 为已打开的文件指针. 此函数相当于调用 fseek(stream, 0, SEEK_SET).
头文件:#include <stdio.h>
函数原型:void rewind(FILE *stream);
参数:
stream:文件流指针
返回值:无
二、printf() 和 fprintf() 输出问题。
printf()输出对象默认是标准输出,标准输出是有缓冲区,也就是说这些缓冲区需要特定的条件才会输出数据。
printf()是一个行缓冲区函数,就是遇到’\n’就会将之前的数据全部输出。
三、fscanf函数
函数作用:从指定的文件stream 获取 指定格式的数据
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
从一个文本中 按照 指定的格式 读取 数据
#include<stdio.h>
int main()
{
char name[256]={0};
int age;
char sex[20]={0};
//1、打开文件
FILE* fp = fopen("./info.txt","rb+");
if(fp == NULL)
{
printf("fopen info.txt error\n");
return -1;
}
//以指定的格式从文本fp中获取数据,存储到变量对应的内存空间上
fscanf(fp,"name:%s age:%d sex:%s",name,&age,sex);
printf("name:%s age:%d sex:%s\n",name,age,sex);
fclose(fp);
return 0;
}
四、标准IO其他函数
1、sprintf() -> 保存一个字符串,拼接字符串。
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
参数:
- str:数据缓冲区
- format:字符串格式
返回值:
- ·字符串总字符个数。
#include<stdio.h>
int main()
{
char *name = "何永成";
int age = 18;
char *sex = "男";
char *city = "广东湛江";
char buf[1024]={0};
//将上面三个数据 拼接在一起,保存到数组中
sprintf(buf,"我的名字是:%s 来自:%s 今年芳龄:%d 性别:%s",name,city,age,sex);
printf("%s\n",buf);
return 0;
}
2、getchar() -> 从标准输入缓冲区中获取一个字符。 -> man 3 getchar (用于清空stdin缓冲区)
#include <stdio.h>
int getchar(void);
参数:getchar()会返回读取到的字符, 若返回 EOF 则表示有错误发生.
返回值:
- 成功: 获取到的字符
- 失败: EOF
#include <stdio.h>
int main(int argc,char *argv[])
{
int a;
char b;
scanf("%d",&a); -> 65 + 回车 -> 65就会放置到a变量中
//把缓冲区的数据清空掉。
//getchar(); //把\n吃掉
//while(getchar()!='\n');
scanf("%c",&b); -> 回车就会放置到b变量。
printf("a = %d\n",a);
printf("a = %c\n",a);
printf("b = %d\n",b);
printf("b = %c\n",b);
return 0;
}
3、getc() -> 从文件指针中获取一个字符 -> man 3 getc
#include <stdio.h>
int getc(FILE *stream);
参数:
- stream:需要获取字符的文件的文件指针。
返回值:
- 成功:获取到的字符
- 失败:EOF
int main()
{
//从键盘上获取一个字符
int ch = getc(stdin);//等价于 getchar()
printf("%c\n",ch);
return 0;
}
getchar与getc的区别:
- getchar() -> 固定从标准输入中获取。
- getc() -> 既可以从标准输入中获取,也可以从文件中获取。
4、 fgetc() -> -> 从文件指针中获取一个字符 -> man 3 fgetc
int fgetc(FILE *stream);等价于 getc()
#include<stdio.h>
int main()
{
FILE *fp = fopen("./info.txt","rb");
if(fp == NULL)
{
printf("fopen error\n");
return -1;
}
int ch = fgetc(fp);
printf("%c\n",ch);
fclose(fp);
return 0;
}
5、 putchar() -> man 3 putchar -> 将一个字符输出到标准输出设备上(屏幕)
int putchar(int c);
参数:
- c: 需要输出的字符
返回值:
- 成功:输出的字符
- 失败:EOF
#include <stdio.h>
int main(int argc,char *argv[])
{
char a = 'x';
int b;
b = putchar(a);
printf("b = %d\n",b);
printf("b = %c\n",b);
return 0;
}
6、 putc() -> man 3 putc -> 将一个字符输出到一个指定的文件中。putc 等价于 fputc
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
参数:
- c: 需要处理的那个字符
- stream: 文件指针
返回值:
- 成功:输出的字符
- 失败:EOF
#include<stdio.h>
int main()
{
//输出一个字符到屏幕终端上
/* putc('a',stdout);
putc('\n',stdout); */
fputc('a',stdout);
fputc('\n',stdout);//如果不加换行符,此时不会输出
//while(1);
return 0;
}
7、puts() -> 将一个字符串输出到屏幕,自带换行符。 -> puts(s) 等价于 printf(“%s\n”,s);
int puts(const char *s); -> 只能是标准输出
参数:
- s 需要处理的字符串
返回值:
- 成功: 非负整数 字符个数+\n
- 失败: EOF
int main()
{
//输出一个字符串到屏幕终端上
puts("hello world");
return 0;
}
8、 fputs() -> 将一个字符串输出到文件。
int fputs(const char *s, FILE *stream); -> 既可以是标准输出,也可以是一个文件
参数:
- s: 需要处理的字符串
- stream:文件指针
返回值:
- 成功: 非负整数 1
- 失败: EOF
#include<stdio.h>
int main()
{
//输出一个字符串到屏幕终端上
//fputs("hello world",stdout);
FILE *fp = fopen("./1.txt","wb");
if(fp == NULL)
{
printf("fopen error\n");
return -1;
}
//输出一个字符串到指定的文件中
fputs("hello world\n",fp);
fclose(fp);
return 0;
}
9、fgets() -> 从文件指针中获取一个字符串。 -> man 3 fgets
char *fgets(char *s, int size, FILE *stream);
参数:
- s: 数据缓冲区
- size: 获取的大小
- stream: 文件指针
返回值:
- 成功: 返回s的值。
- 失败: NULL
int main(int argc,char *argv[])
{
char buf[100] = {0};
fgets(buf,100,stdin); //此时也会获取换行符\n
printf("buf:%s",buf);
return 0;
}
五、获取文件属性stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
上面三个函数的功能完全一样,区别就是 stat函数传递的就是文件名字,fstat函数传递的是文件描述符,而lstat函数则可以获取链接文件本身的属性。
作用:
- 获取文件的属性
参数:
- path: 你要获取哪个文件的属性,将该文件的路径名传递进来
- buf : 存放文件属性的结构体
- fd : 你要获取哪个文件的属性,将该文件的文件描述符传递进来
返回值:
- 成功返回 0
- 失败返回 -1 ,错误被标记
struct stat {
dev_t st_dev; /* ID of device containing file */常规文件的ID
ino_t st_ino; /* inode number */ 文件节点数字 i-node
mode_t st_mode; /* protection */ 文件的类型和文件的权限
nlink_t st_nlink; /* number of hard links */ 硬链接数
uid_t st_uid; /* user ID of owner */ 用户ID
gid_t st_gid; /* group ID of owner */ 组ID
dev_t st_rdev; /* device ID (if special file) */特殊设备ID
off_t st_size; /* total size, in bytes */ 文件大小,特殊文件判断不了
blksize_t st_blksize; /* blocksize for file system I/O */文件系统IO缓冲区的大小,通常是1024
blkcnt_t st_blocks; /* number of 512B blocks allocated */有几块数据,文件系统块大小的单位是512字节
time_t st_atime; /* time of last access 访问(读、写、执行)*/ 文件最近一次被存取或被执行的时间
time_t st_mtime; /* time of last modification(写) */ 文件最后一次被修改的时间
time_t st_ctime; /* time of last status change */ 文件属性更改时间
};
如果你想要获取文件类型:把st_mode传给以下函数就能判断文件类型,如果是则返回真,不是返回假
S_ISREG(m) is it a regular file? 普通文件
S_ISDIR(m) directory? 目录文件
S_ISCHR(m) character device? 字符设备驱动文件
S_ISBLK(m) block device? 块设备驱动文件
S_ISFIFO(m) FIFO (named pipe)? 管道文件
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.) 软链接文件
S_ISSOCK(m) socket? (Not in POSIX.1-1996.) 套接字文件
1、案例1–获取文件大小
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc ,char**argv)
{
if(argc<2){
printf("请先传递文件名\n");
return -1;
}
struct stat info;
stat(argv[1],&info);
printf("filesize:%ld\n",info.st_size);
return 0;
}
2、判断文件类型
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc ,char**argv)
{
if(argc<2){
printf("请先传递文件名\n");
return -1;
}
struct stat info;
stat(argv[1],&info);
if(S_ISREG(info.st_mode)){
printf("普通文件\n");
}
else if(S_ISDIR(info.st_mode)){
printf("目录文件\n");
}
else if(S_ISCHR(info.st_mode)){
printf("字符设备文件\n");
}
return 0;
}
六、时间日期函数
1、time
#include <time.h>
time_t time(time_t *tloc);
作用:
- 取得目前的时间,也就是从公元 1970 年 1 月 1 日的 UTC 时间从 0 时 0 分 0 秒算起到现在所经过的秒数。
参数:
- tloc : 如果 tloc 并非空指针的话, 此函数也会将返回值存到 t 指针所指的内存.
返回值:
- 成功则返回秒数
- 失败则返回((time_t)-1)值, 错误原因存于 errno 中.
#include<stdio.h>
#include <time.h>
int main()
{
time_t tloc;
time_t ret = time(&tloc);
printf("ret:%ld tloc:%ld\n",ret,tloc);
//ret:1644845242 tloc:1644845242
return 0;
}
2、ctime
#include <time.h>
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
作用:
- 将时间和日期以字符串格式表示,一般配合time()函数使用
参数:
- timep:传递time()函数的返回值
- buf: 将转换后的格式存储到这里
返回值:
- 返回一字符串表示目前当地的时间日期.
#include<stdio.h>
#include <time.h>
int main()
{
char timebuf[1024]={0};
time_t ret = time(NULL);
//char *curTime = ctime(&ret);
char *curTime = ctime_r(&ret,timebuf);
printf("curTime:%s \n",curTime);//curTime:Tue May 11 05:34:47 2021
printf("timebuf:%s \n",timebuf);//timebuf:Tue May 11 05:34:47 2021
return 0;
}
3、gmtime
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
作用:
- 取得目前的时间和日期,将参数 timep 所指的 time_t 结构中的信息转换成真实世界所使用的时间日期表示方法, 然后将结果由结构 tm 返回.
参数:
- timep:time()函数的返回值
- result:存储转换之后的结果
返回值:
- 返回目前的时间,存储在结构tm中
struct tm
{
int tm_sec; //代表目前秒数, 正常范围为 0-59, 但允许至 61 秒
int tm_min; //代表目前分数, 范围 0-59
int tm_hour; //从午夜算起的时数, 范围为 0-23
int tm_mday; //目前月份的日数, 范围 01-31
int tm_mon; //代表目前月份, 从一月算起, 范围从 0-11
int tm_year; //从 1900 年算起至今的年数
int tm_wday; //一星期的日数, 从星期一算起, 范围为 0-6
int tm_yday; //从今年 1 月 1 日算起至今的天数, 范围为 0-365
int tm_isdst; //日光节约时间的旗标
};
#include<stdio.h>
#include <time.h>
int main()
{
time_t tloc;
time_t ret = time(&tloc);
struct tm *curDateTime = gmtime(&ret);
printf("date:%d:%d:%d\n",(1900+curDateTime->tm_year),
(1+curDateTime->tm_mon),
curDateTime->tm_mday);
printf("time:%d:%d:%d\n",curDateTime->tm_hour,
curDateTime->tm_min,
curDateTime->tm_sec);
return 0;
}
4、localtime
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
作用:
- 取得当地目前时间和日期, 此函数返回的时间日期已经转换成当地时区.
参数:
- timep:time()函数的返回值
- result:存储转换之后的结果
返回值:
- 返回目前的时间,存储在结构tm中
#include<stdio.h>
#include <time.h>
int main()
{
time_t tloc;
time_t ret = time(&tloc);
struct tm *curDateTime = localtime(&ret);
printf("date:%d:%d:%d\n",(1900+curDateTime->tm_year),
(1+curDateTime->tm_mon),
curDateTime->tm_mday);
printf("time:%d:%d:%d\n",curDateTime->tm_hour,
curDateTime->tm_min,
curDateTime->tm_sec);
return 0;
}
5、asctime
char *asctime(const struct tm * timeptr);
函数作用:
- 将结构体所指的本地时间 转换成 字符串本地时间
返回值:
- 返回本地时间
#include <time.h>
#include <stdio.h>
int main()
{
time_t t = time(NULL);
//得到的是本地时间
struct tm* curTime = localtime(&t);
printf("date %d/%d/%d ",(1900+curTime->tm_year),
(1+curTime->tm_mon),
curTime->tm_mday);
printf("time %d:%d:%d\n",curTime->tm_hour,
curTime->tm_min,
curTime->tm_sec);
//将结构体所指的本地时间 转换成 字符串本地时间
char *curTimeStr = asctime(curTime);
printf("curTimeStr:%s\n",curTimeStr);
return 0;
}
七、目录检索
访问文件,可以得到文件里面的数据。
访问目录,可以得到目录下的目录项,该目录项包含文件的名字,文件的类型。
1)如何打开一个目录? -> opendir() -> man 3 opendir
#include <sys/types.h>
#include <dirent.h> -> 目录IO专属头文件
DIR *opendir(const char *name);
参数:
- name:目录的路径
返回值:
- 成功: 目录流指针
- 失败: NULL,错误被标记
注意:新打开一个目录,默认目录流指针都是指向目录第一个目录项。
问题: 使用opendir打开一个目录,就等价于切换到该目录下吗?
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc,char*argv[])
{
system("pwd");
DIR *dirfp = opendir(argv[1]);
if(dirfp == NULL)
{
perror("opendir error");
return -1;
}
system("pwd");
return 0;
}
2)如何在程序中切换路径? -> 在程序中切换,而不是在ubuntu下切换 -> chdir() -> man 2 chdir
//切换工作路径
#include <unistd.h>
int chdir(const char *path);
参数:
- path: 需要切换的路径 (注意:这个地方不是填目录流指针)
返回值:
- 成功:0
- 失败:-1
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc,char*argv[])
{
system("pwd");
chdir("./aa/");//切换工作路径到当前目录下的子目录aa里面
system("pwd");
return 0;
}
3)如何读取目录下的内容? -> readdir() -> man 3 readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:
- dirp: 目录流指针。
返回值:
- 成功:目录项指针 struct dirent *
- 失败:NULL -> 读完了目录中的所有项,还继续读,那么就会返回NULL。
struct dirent
{
ino_t d_ino; // 文件索引号
off_t d_off; // 目录项偏移量
unsigned short d_reclen; // 该目录项大小
unsigned char d_type; // 文件类型
char d_name[256]; // 文件名
};
d_type: 在/usr/include/dirent.h 中定义了
DT_BLK 6 This is a block device. -> 块设备
DT_CHR 2 This is a character device. -> 字符设备
DT_DIR 4 This is a directory. -> 目录
DT_FIFO 1 This is a named pipe (FIFO). -> 管道
DT_LNK 10 This is a symbolic link. -> 链接
DT_REG 8 This is a regular file. -> 普通文件
DT_SOCK 12 This is a UNIX domain socket. -> 套接字文件
DT_UNKNOWN 0 The file type is unknown. -> 未知类型
4)关闭目录。 -> closedir() -> man 3 closedir
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
参数:
- dirp: 目录流指针。
返回值:
- 成功:0
- 失败:-1
注意:每次调用readdir函数,只能读取一个文件,如果你想要读取某个目录下所有的文件,可以不断循环调用readdir函数
// 案例:打印指定目录下的文件名
#include<stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include<string.h>
int main(int argc,char*argv[])
{
//打开目录
DIR *dirfp = opendir(argv[1]);
if(dirfp == NULL)
{
perror("opendir error");
return -1;
}
while(1)
{
//读取目录
struct dirent *info = readdir(dirfp);
if(info == NULL)//如果读取完了 退出
break;
printf("d_ino:%ld d_off:%ld d_reclen:%d d_name:%s d_type:%d\n",info->d_ino,info->d_off,info->d_reclen,info->d_name,info->d_type);
}
//关闭目录文件
closedir(dirfp);
return 0;
}
// 将指定目录下的bmp结尾的普通文件路径名(./1.bmp ./2.bmp ./3.bmp),存储到数组中
#include<stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include<string.h>
#define BMP_NUMBER 100 //最大只能支持读取100张图片
#define BMPDIR "./resource"
//效果:从 指定的目录中读取 所有bmp图片的名字,存储到数组中
int main()
{
char bmpName[BMP_NUMBER][256]={0};//存储图片的名字
int bmpCount=0; //记录当前加载进来的bmp图片数量
int i;
//打开目录
DIR *dirfp = opendir(BMPDIR);
if(dirfp == NULL)
{
perror("opendir error");
return -1;
}
while(1)
{
//读取目录
struct dirent *info = readdir(dirfp);
if(info == NULL)//如果读取完了 退出
break;
printf("d_name:%s\n",info->d_name); //info->d_name ---1.bmp
//去除 . 和 .. 而且 文件 是 普通 文件
if(info->d_name[0] != '.' && info->d_type == DT_REG)
{
//bmpName[0]---> ./reource/1.bmp
//将图片的路径 和 名字 拼接起来
char pathName[256]={0};
sprintf(pathName,"%s/%s",BMPDIR,info->d_name);
strcpy(bmpName[bmpCount++],pathName); // info->d_name[256] char d_name[256] = "1.bmp"
}
}
//将存储到数组中所有的图片名字都打印出来
for(i=0; i<bmpCount; i++)
{
printf("bmpName[%d]:%s\n",i,bmpName[i]);
}
//关闭目录文件
closedir(dirfp);
return 0;
}