“这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战”
预定义流
在程序开始之前,创建三个文件描述符,分别0、1、2对应的标准输入,标准输出,标准错误输出,同时也在其基础上封装出了三个预定义流指针
- 标准输入: stdin ----》键盘文件
- 标准输出: stdout ----》终端文件
- 标准错误输出:stderr ----》终端文件
代码:
int main(int argc, const char *argv[])
{
//stdin
char buf[123]={0};
fgets(buf,123,stdin);//从标准输入流中读取数据
printf("buf = %s\n",buf);
//标准输出
fprintf(stdout,"hello world%s","112123");
//标准错误输出
fprintf(stderr,"hello world%s","112123");
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
hello world
buf = hello world
hello world112123hello world112123linux@ubuntu:~/demo/test/IO/test$
缓冲区
《面试题》:缓冲区是什么?标准IO在文件IO的基础上封装的一片存放数据的地址(一般用来存放不着急的数据),等到缓冲区这个地址中的数据存满,或者说程序员手动刷新这片空间,空间中的数据会被调用文件IO操作。
缓冲区一共分为三大类:
全缓冲
一般为对文件的操作,全缓冲区大小为4096个字节。
(附加:tail -f + 文件名):可以另开一个终端动态查看文件内容
刷新缓冲区的条件:
-
缓冲区满刷新缓冲区
-
程序结束刷新缓冲区
-
程序手动刷新缓冲区
fflush
- 原型:
int fflush(FILE *stream); - 功能:刷新缓冲区
- 参数:目标文件流指针
- 返回值:
- 成功:0
- 返回:EOF
测试代码:
int main(int argc, const char *argv[])
{
FILE * fp = fopen("./1.txt","w");
//fopen、open的打开方式只针对与打开时的一瞬间
if (NULL ==fp)
{
perror("fopen");
return -1;
}
fprintf(fp,"hello world");
fflush(fp);
while(1);
return 0;
}
行缓冲
只有两个是行缓冲,标准输入,标准输出。行缓冲大小为1024个字节
刷新行缓冲的条件:
-
缓冲区满则刷新缓冲区
-
缓冲区遇到\n则刷新缓冲区
-
当标准输入和标准输出一方要使用缓冲区时,正在使用的一方需要让出缓冲区,给需要用的一方使用
-
fflush刷新缓冲区
-
程序结束
测试代码:
1.验证缓存区大小
#include <stdio.h>
int main(int argc, const char *argv[])
{
//航缓冲大小为1024个字节
//验证缓冲区大小
int i = 0;
for ( i = 0; i < 1024; i++)
{
printf("1");//每次向标准输出输入1个字节
}
while(1);//起阻塞作用
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
^C
//1024时不会打印
linux@ubuntu:~/demo/test/IO/test$ ./a.out
111111111111......1111111111111111
//1025时可以打印
2.验证'\n'可以刷新行缓存区
#include <stdio.h>
int main(int argc, const char *argv[])
{
//验证'\n'刷新缓存区
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("1");//每次向标准输出输入1个字节
}
printf(\n"");//刷新
while(1);//阻塞
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
1111111111
3.验证标准输入输出刷新缓存区
#include <stdio.h>
int main(int argc, const char *argv[])
{
//验证'\n'刷新缓存区
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("1");//每次向标准输出输入1个字节
}
int n;
scanf("%d",&n);
/*printf("hello");
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
11111111112
linux@ubuntu:~/demo/test/IO/test$ ./a.out
1111111111hellolinux@ubuntu:~/demo/test/IO/test$
4.ffiush也可以,就不上代码了。
无缓冲
一般为标准错误输出,一般用于比较着急的数据,所以不会进入缓冲区。直接调用文件IO。
一般使用方式:fprintf(stderr,"hello world");
注意:自己做错误报告,没有错误码时,后续报错信息:使用这种方式,避免printf忘记写'\n'没有输出;
时间函数
1.time
-
头文件:
#include <time.h> -
原型:
time_t time(time_t *tloc); //以后要指针时,可以定义变量取地址放在这
time_t mytime; time(mytime); struct tm *localtime(const time_t *timep); struct tm * mytm = localtime(&mytime);typedef long time_t -
功能:获取现在计算机时间到1970-01-01-00-00-00的秒数
-
参数:
tloc:用来存放获取到的描述的变量的地址,该值可以填写NULL ,填写NULL的话获取秒数通过返回值来获取
-
返回值:
- 成功返回1970-01-01-00-00-00到现在的秒数
- 失败返回:(time_t)-1
代码:
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
time_t mytime;
mytime = time(NULL);
printf("当前时间到1970年的时间为%ld\n",mytime);
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
当前时间到1970年的时间为1630154859
//利用秒数就可以算出时间
2.ctime
- 原型:
char *ctime(const time_t *timep); - 功能:按照固定的格式将现在的时间转换出来
- 参数:time_t类型的变量的地址
- 返回值:
- 成功返回封装好的字符串的首地址
- 失败返回:NULL
- 注意:使用ctime时输出的格式时固定,用户无法更改
代码:
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
time_t mytime;
mytime = time(NULL);//time函数获取到秒数
printf("当前时间到1970年的时间为%ld\n",mytime);
//使用ctime将秒数转换成固定的字符串
char * buf = ctime(&mytime);
printf("现在的时间为%s",buf);
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
当前时间到1970年的时间为1630155367
现在的时间为Sat Aug 28 05:56:07 2021
3.localtime
- 原型:
struct tm *localtime(const time_t *timep); - 功能:会将timep转换成现在的时间,将各项数据存放之结构中
- 参数:秒数变量的地址
- 返回值:
- 成功:会返回一个已经封装好的tm结构体
- 失败:NULL
suruct tm结构体:
struct tm {
int tm_sec; /* Seconds (0-60) 有1秒为闰秒用于时间校准*/
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) 使用时需要+1 */
int tm_year; /* Year - 1900 = 70 -使用时需要 + 1900-*/万年虫
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* 夏令营时间 */
};
代码:
#include <time.h>
int main(int argc, const char *argv[])
{
time_t mytime;
mytime = time(NULL);//time函数获取到秒数
//调用localtime函数进行tm结构体封装
struct tm * mytm = localtime(&mytime);
if(NULL == mytm)
{
perror("localtime");
return -1;
}
printf("当前的时间为:%d年-%d月-%d日--%d:%d:%d\n",
mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,
mytm->tm_hour,mytm->tm_min,mytm->tm_sec);
return 0;
}
📝练习:编写日志文件,将时间每隔1秒中写入文件之中,sleep(1)
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
//打开一个文件
FILE *fp = fopen("./1.txt","a");
if(NULL == fp)
{
perror("fopen");
return -1;
}
while(1)
{
//获取当前的距离1970年的秒数
time_t mytime = time(NULL);
//开始转换时间
struct tm * mytm = localtime(&mytime);
fprintf(fp,"当前的时间为:%d年-%d月-%d日--%d:%d:%d\n",
mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,
mytm->tm_hour,mytm->tm_min,mytm->tm_sec);
fflush(fp);
sleep(1);
}
return 0;
}
stat获取文件状态属性
- 头文件:
#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>
- 原型:
int stat(const char *pathname, struct stat *statbuf); - 功能: 获取文件的状态属性
- 参数:
- pathname:目标文件的路径及名称
- statbuf :存放文件属性信息的结构体地址
- 返回值:
- 成功返回:0
- 失败返回:-1
stat 结构体
struct stat {
dev_t st_dev; /* 设备文件ID号*/
ino_t st_ino; /* Inode号 对文件的一个编号*/
mode_t st_mode; /* 文件的类型和权限 */
nlink_t st_nlink; /* 链接数 */
uid_t st_uid; /* 所属用户ID */
gid_t st_gid; /* 组ID号 */
dev_t st_rdev; /*字符设备文件ID号 */
off_t st_size; /* 文件的大小 */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
struct timespec st_atim; /* 最后一次访问的时间 */
struct timespec st_mtim; /* 最后一次修改的时间 */
struct timespec st_ctim; /* 最后一次状态改变的时间 */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
struct timespec {
time_t tv_sec; /* 秒数 */
long tv_nsec; /* 纳秒 */
};
文件的类型
st_mode
| r | W | X | R | W | x | R | W | X |
|---|---|---|---|---|---|---|---|---|
| 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 宿主 | 组员 | 其他 |
|---|---|---|
| 876 | 543 | 210 |
文件类型定义了以下掩码值:
st_mode有文件的信息,和0170000相与会一一对应。
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
文件权限定义了以下掩码值:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
📝打印文件类型和权限
代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
int main(int argc, const char *argv[])
{
struct stat mystat;
if(stat(argv[1],&mystat) == -1)
{
perror("stat");
return -1;
}
//开始打印文件的类型
switch (mystat.st_mode & S_IFMT)
{
case S_IFSOCK:
printf("s");
break;
case S_IFLNK:
printf("l");
break;
case S_IFREG:
printf("-");
break;
case S_IFBLK:
printf("b");
break;
case S_IFDIR:
printf("d");
break;
case S_IFCHR:
printf("c");
break;
case S_IFIFO:
printf("p");
break;
}
//开始打印文件的权限
if((mystat.st_mode & S_IRUSR) !=0)
{
printf("r");
}else{
printf("-");
}
if((mystat.st_mode & S_IWUSR) !=0)
{
printf("w");
}else{
printf("-");
}
if((mystat.st_mode & S_IXUSR) !=0)
{
printf("x");
}else{
printf("-");
}
//开始判断组权限
if((mystat.st_mode & S_IRGRP) !=0)
{
printf("r");
}else{
printf("-");
}
if((mystat.st_mode & S_IWGRP) !=0)
{
printf("w");
}else{
printf("-");
}
if((mystat.st_mode & S_IXGRP) !=0)
{
printf("x");
}else{
printf("-");
}
//开始判断其他用户权限
if((mystat.st_mode & S_IROTH) !=0)
{
printf("r");
}else{
printf("-");
}
if((mystat.st_mode & S_IWOTH) !=0)
{
printf("w");
}else{
printf("-");
}
if((mystat.st_mode & S_IXOTH) !=0)
{
printf("x");
}else{
printf("-");
}
printf("\n");
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out test.c
-rwxrwxrwx
用户的信息
- 头文件:
#include <sys/types.h>#include <pwd.h>
- 原型:
struct passwd *getpwuid(uid_t uid); - 功能:根据ID号查询用户信息然后封装结构体
- 参数:用户ID号
- 返回值:
- 成功返回已经封装好的结构体passwd
- 失败返回NULL
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
组的信息
- 头文件:
- #include <sys/types.h>
- #include <grp.h>
- 原型:struct group *getgrgid(gid_t gid);
- 参数:组ID号
- 返回值:
- 成功返回已经封装好的结构体地址
- 失败返回NULL
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* 以NULL结尾的一个数组,里面存放的是组员的名字 */
};
代码:
//补在上面代码后面就行了
//链接数
printf(" %ld ",mystat.st_nlink);
//打印用户名
struct passwd * mypass = getpwuid(mystat.st_uid);
if(NULL ==mypass)
{
perror("getpwuid");
return -1;
}
printf("%s ",mypass->pw_name);
//打印组名
struct group * mygr = getgrgid(mystat.st_gid);
if (NULL ==mygr)
{
perror("getgrgid");
return -1;
}
//打印组命
printf("%s ",mygr->gr_name);
//打印大小
printf("%ld",mystat.st_size);
//打印时间
struct tm * mytm = localtime(&mystat.st_ctim.tv_sec);
printf(" %d月 %d %02d:%02d",mytm->tm_mon+1,mytm->tm_mday,mytm->tm_hour,mytm->tm_min);
printf("%s\n",argv[1]);
linux@ubuntu:~/demo/test/IO/test$ ./a.out test.c
-rwxrwxrwx 1 linux linux 2537 8月 28 07:31test.c
目录操作
opendir
- 头文件:
#include <sys/types.h>#include <dirent.h>
- 原型:
DIR *opendir(const char *name);- DIR :目录流指针-----打开目录的标志,共给后续操作使用
- 参数:name:要开的的目录的路径
- 反回值:
- 成功返回目录流指针
- 失败返回 NULL
readdir
-
头文件:
#include <dirent.h> -
原型:
struct dirent *readdir(DIR *dirp); -
功能:读取目录文件
-
参数:目标目录流指针
-
返回值:
- 成功返回一个已经封装好的目录信息结构体的地址
- 失败返回NULL
- 读到文件末尾返回NULL
-
注意:读取时以文件为单位,不能一起将所有文件信息读出来,只能依次读取 每次读取都会返回一个封装好的结构体。
struct dirent {
ino_t d_ino; /* Inode号用于索引列表使用--文件的编号 */
off_t d_off; /* 文件的偏移值 */
unsigned short d_reclen; /* 文件名字的长度 */
unsigned char d_type; /* 文件的类型*/
char d_name[256]; /* 以NULL为终止字符的文件名字符串 */
};
closedir
- 头文件:
#include <sys/types.h>#include <dirent.h>
- 原型:int closedir(DIR *dirp);`
- 功能:关闭一个目录流指针
- 参数:目标目录流指针
- 返回值:
- 成功返回0 ;
- 失败返回-1;
代码:
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
int main(int argc, const char *argv[])
{
//打开一个目录
DIR *mydir = opendir(".");
if(NULL == mydir)
{
perror("opendir");
return -1;
}
//开始读取文件
struct dirent * mydirent; //要顶一个struct dirent *指针接收read返回值
while(NULL != (mydirent = readdir(mydir)))
{
printf("文件名为 %s\n",mydirent->d_name);
}
closedir(mydir);
return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out
文件名为 .
文件名为 test.c
文件名为 a.out
文件名为 core
文件名为 ..
文件名为 1.txt
📝练习:
实现ls -l 加参数和不加参数都可以