IO简介
【1】什么是IO
#include <stdio.h>
std:standard 标准的
i:input 输入 数据从外部存储设备输入到内存中
o:output 输出 数据从内存输出到外部存储设备中。
存储设备:
1.硬盘:机械硬盘和固态硬盘
2.内部存储设备:内存:SDROM DDR4 用于缓存硬盘上的数据。
总结:IO就是数据从硬盘到内存,内存到硬盘的流动
【2】IO的分类
1)文件IO
文件IO:由系统提供的基本IO函数,与操作系统绑定的,又称之为系统调用(从用户空间到内核通过系统调用实现)。
使用文件IO每输入一次数据,CPU模式会切换一次。
例子:
| windos | linux |
| file_read file_write | read write |
注意:
1.文件IO的复用性低
2.文件IO涉及到用户空间到内核空间的切换,cpu模式切换,C代码调用汇编指令等等,属于以一种耗时操作,应该尽量减少文件IO的使用。
2)标准IO
标准IO是根据ANSI标准,对文件IO进行了二次封装,比如:scanf,printf等
特点:
1.提高代码的可移植性和复用性;
2.提高输入输出的效率
设置了一个缓冲区,缓冲区满或者满足一定条件后,调用文件IO,陷入内核空间,由内核完成对硬件的操作,大大减少了对文件IO的调用。下图为标准IO和文件IO的对比。
2.标准IO
【1】流和流指针
流(stream):将数据一个一个的移入或者移出文件的形式,叫做字节流。
流指针(FILE*):每打开一个文件,就会在内存中申请一片空间(缓冲区),管理这片空间的变量都存储在FILE结构体,FILE结构体是由系统定义的,我们只要拿过来用就可以了。
1. 查看FILE结构体成员
第一步:$ vi -t FILE
//vi -t 可以用于查看系统提供的数据类型,结构体,宏定义,全局变量
第二步:输入1
# pri kind tag file
1 F t FILE /usr/include/stdio.h
typeref:struct:_IO_FILE
typedef struct _IO_FILE FILE;
第三步:找到typedef struct __IO_FILE
第四步:$ vi -t __IO_FILE
第五步:输入1
# pri kind tag file
1 F s _IO_FILE /usr/include/libio.h
struct _IO_FILE {
//或者在第三步鼠标悬浮在 _IO_FILE上时,ctrl+鼠标右键直接进入,进行第五步
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/*The following pointers correspond to the C++ streambuf protocol. */
/* Note:Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* Start of get area.*/
char* _IO_read_base; /* End of get area.*/
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* 缓冲区的起始地址 */
char* _IO_buf_end; /* 缓冲区的结束地址 */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /*Pointer to start of non-current get area. */
char *_IO_backup_base;/*Pointer to first valid character of backup area*/
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; /*文件描述符*/
2. 标准输入输出流
在main函数启动之前,会默认打开三个流指针。
stdin 标准输入 从终端获取数据
stdout 标准输出 将数据输出到终端
stderr 标准错误输出
通过 vi -t查看
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
【2】标准IO函数
1.常见的标准IO函数
fopen / fclose 打开/关闭一个文件;
fprintf / fscanf 将数据输入到文件中/从文件中获取数据
fputc / fgetc 将单个字符的输入到文件中/从文件中读取字符
fputs / fgets 将字符串输入到文件中/从文件中读取字符串
fwrite / fread 将数据以二进制的形式输出到文件中 / 从文件中读取二进制数据;
fseek 偏移流指针
2. man手册
$ man man 查看man手册
1 Executable programs or shell commands
2 System calls (functions provided by the kernel)
3 Library calls (functions within program libraries)
man 1 卷 linux中的可执行文件或shell指令
man 2 卷 系统调用(文件IO)
man 3 卷 库调用(标准IO)
man查找的时候,会默认从第一卷开始查找
3. 标准IO函数的使用
1)fopen
功能:打开一个文件;
头文件:
#include <stdio.h>
原型:
FILE *fopen(const char *path, const char *mode);
参数:
char *path:指定要打开的文件路径+文件名(eg:"./1.c");
char *mode:打开方式(eg:"r+");
r 以读的方式打开;
如果文件不存在,则打开失败;
r+ 以读写的方式打开;
如果文件不存在,则打开失败;
w 以写的方式打开;
如果文件不存在,则创建文件;
如果文件存在,则清空文件;
w+ 以读写的方式打开;
如果文件不存在,则创建文件;
如果文件存在,则清空文件;
a 以写的方式打开;
如果文件不存在,则创建文件;
如果文件存在,则以追加的方式写;
a+ 以读写的方式打开;
如果文件不存在,则创建文件;
如果文件存在,则以追加的方式写;
返回值:
成功,返回打开的文件流指针;
失败,返回NULL,同时更新错误码errno;
2)perror
功能:通过错误码errno,打印错误信息;
头文件:
#include <stdio.h>
原型:
void perror(const char *s);
参数:
char *s:用于提示的字符串(eg:"fopen")
#include <errno.h>
int errno; 本质上是一个全局变量,对文件进行操作的时候,会出现各种错误,例如文件不存在,文件权限不足
errno中已经定义好了各种数值,与错误相对应;
errno路径:/usr/include/asm-generic 目录下 errno.h errno-base.h
3)fclose
功能:关闭一个文件;
头文件:
#include <stdio.h>
原型:
int fclose(FILE *fp);
参数:
FILE *fp:要关闭的流指针;
返回值:
成功,返回0;
失败,返回EOF(其实就是-1),更新errno
思考:能否用一个流指针对文件进行重复fopen和fclose
答案:不能
1.调用fopen时,会去堆空间申请一片缓冲区,返回的流指针中就存储了这片空间的首地址。
2.不要重复打开一个文件,会造成资源浪费,而且有可能会造成内存泄漏,linux内核文件的打开次数是有限的。
3.调用fclose时,会通过free释放指定的堆空间,再次调用fclose,会造成重复释放。
例子
#include <stdio.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("参数不足,请输入打开方式\n");
return -1;
}
FILE* fp = fopen("./fopen.txt", argv[1]);
if(NULL == fp)
{
printf("%d\n", errno);
perror("fopen");
printf("打开失败\n");
return -1;
}
printf("打开成功\n");
if(fclose(fp)<0)
{
perror("fclose");
return -1;
}
printf("关闭成功\n");
return 0;
}
4)fprintf
功能:将数据格式化输出到文件中;
头文件:
#include <stdio.h>
原型:
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
参数:
FILE *stream:要输出到文件的文件流指针;
char *format:格式化输出;
...:不定参数;
返回值:
成功,返回输出的数据个数,字节为单位
失败,返回负数,更新errno;
例子
//2.输出到文件中
if(fprintf(fp, "hello world %d", __LINE__)<0)
{
perror("fprintf");
return -1;
}
5)fscanf
功能:从文件中格式化获取数据;
头文件:
#include <stdio.h>
原型:
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
参数:
FILE *stream:需要获取数据的文件流指针;
char *format:格式化输入;
...:不定参数;
返回值:
成功,返回获取到的数据个数;
失败或者读到文件结尾,返回EOF,更新errno;
注意:
1.fscanf和scanf类似,遇到空格,TAB,回车等均会停止读入
2.fscanf读到文件结尾时返回EOF
例子
//1.打开文件
FILE *fp = fopen("./fprintf.txt", "r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
//2.文件中读取数据
char buf[15] = "";
char buf1[15] = "";
fscanf(fp, "%s %s", buf, buf1);
printf("buf = %s %s\n", buf, buf1);
int res = fscanf(fp, "%s %s", buf, buf1);
printf("res = %d\n",res);
if(res < 0)
{
perror("fscanf");
return -1;
}
//3.关闭文件
fclose(fp);
return 0;
6)fputc
功能:将单个字符输出到文件中;
头文件:
#include <stdio.h>
原型:
int fputc(int c, FILE *stream);
参数:
int c:指定要输出的字符;
FILE *stream:需要写入的文件的流指针;
返回值:
成功,返回输出字符的ascii值;
失败,返回EOF,更新errno;
例子
fputc(97, fp);
fputc('a', fp);
int c = 'a';
if(fputc(c, fp) < 0)
{
perror("fputc");
return -1;
}
7)fgetc
功能:从文件中获取单个字符;
头文件:
#include <stdio.h>
原型:
int fgetc(FILE *stream);
参数:
FILE *stream:要从文件获取的文件流指针;
返回值:
成功,返回获取到的字符;
失败,或者读到文件末尾时返回EOF,更新errno;
注意:
1.读到文件末尾时返回EOF
2.区别getchar和fgets:
char c=getchar();
int c=fgetc(fd);
例子
int c = 0;
c = fgetc(fp);
if(c == EOF)
{
perror("fgetc");
return -1;
}
小练习
1.用fgetc函数,计算文件大小。
2.写一个函数,计算一个文件中共有几行。
提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符)
3.使用fgetc和fputc实现文件拷贝,例如将a.c拷贝成b.c
第1题
1.用fgetc函数,计算文件大小。
#include <stdio.h>
int file_size(FILE* fp)
{
int size = 0;
//循环读取文件字符,直到返回错误
/*
while(fgetc(fp)!=EOF)
size++;
*/
while(1)
{
int c = fgetc(fp);
if(c == EOF)
{
break;
}
size++;
}
return size;
}
int main(int argc, const char *argv[])
{
//1.外部传参传入文件名
if(argc < 2)
{
printf("请输入文件\n");
return -1;
}
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
int size = file_size(fp);
printf("%d\n",size);
fclose(fp);
return 0;
}
第2题
2.写一个函数,计算一个文件中共有几行。
提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符);
//获取文件行数
int file_line(FILE* fp)
{
int line = 0;
int c = 0;
while((c = fgetc(fp)) != EOF)
{
if('\n' == c)
{
line++;
}
}
return line;
}
int main(int argc, const char *argv[])
{
//1.外部传参传入文件名
if(argc < 2)
{
printf("请输入文件\n");
return -1;
}
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
int line = file_line(fp);
printf("line=%d\n", line);
fclose(fp);
return 0;
}
第3题
3.使用fgetc和fputc实现文件拷贝,例如将a.c拷贝成b.c;
#include <stdio.h>
void file_copy(FILE* rp, FILE* wp)
{
char c = 0;
while((c=fgetc(rp)) != -1)
{
fputc(c, wp);
}
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
return -1;
}
FILE* fp_r = fopen(argv[1], "r");
if(NULL == fp_r)
{
perror("fopen");
return -1;
}
FILE* fp_w = fopen("./b.c", "w");
file_copy(fp_r, fp_w);
fclose(fp_r);
fclose(fp_w);
return 0;
}
8)缓冲区
标准IO的内容都是存储在缓冲区中,然后输出到硬件中。
1.全缓冲
- 操作对象
对普通文件进行操作(通过fopen打开的文件),通过FILE*fp流指针维护一个4K大小的缓冲区;即文件打开的默认方式为全缓冲 - 大小:
4K = 4*1024 = 4096byte;fp->_IO_buf_end - fp->_IO_buf_base = 4096byte
- 刷新缓冲区
1)缓冲区满
2)用fflush函数强制刷新缓冲区
3)关闭流指针#include <stdio.h> int fflush(FILE *stream);
4)程序正常退出
5)调用exit函数
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp = fopen("./fullbuf.txt", "w");
//1.缓冲区满
/******************************/
int i = 0 ;
while(i++<4096+1)
{
fputc('a', fp);
}
/******************************/
//2.用fflush函数
/******************************/
fputc('b', fp);
fputc('b', fp);
fputc('b', fp);
fflush(fp);
/******************************/
//3.fclose
/******************************/
fputc('c', fp);
fputc('c', fp);
fputc('c', fp);
fclose(fp);
/******************************/
//4.程序正常退出 return 0;
/******************************/
fputc('d', fp);
fputc('d', fp);
fputc('d', fp);
return 0;
/******************************/
//5.exit
/******************************/
fputc('e', fp);
fputc('e', fp);
fputc('e', fp);
exit(100);
while(1);
fclose(fp);
return 0;
}
2.行缓冲
- 操作对象
标准输入(stdin),标准输出(stdout) - 大小
1K = 1024bytestdin->_IO_buf_end - stdin->_IO_buf_base = 1024byte
- 刷新缓冲区
1)缓冲区满
2)用fflush函数强制刷新缓冲区
3)关闭流指针#include <stdio.h> int fflush(FILE *stream);
4)程序正常退出
5)调用exit函数
6)遇到‘\n’字符
注意:
1.只有标准输入/输出(stdin和stdout才可以使用行缓冲)
2.在全缓冲的基础上,行缓冲多了一种用换行符’\n’刷新缓冲区的方式
3.由于系统优化,如果只是打开文件,不操作,并不会真正的申请空间。
3.无缓冲
- 操作对象
标准错误输出,stderr
其实perror调用的就是stderr; - 缓冲区大小为0
9)fputs
功能:将字符串输出到文件中;
头文件:
#include <stdio.h>
原型:
int fputs(const char *s, FILE *stream);
参数:
char *s:要输出的字符串首地址;
FILE *stream:要输入到哪个文件的文件流指针;
返回值:
成功,返回非负数;
失败,返回EOF,更新errno
例子
FILE* fp = fopen("./fgets.txt", "w+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
fputs("hello\n", fp);
fputs("world\n", fp);
fclose(fp);
10)fgets
功能:从文件中获取字符串(一行一行地读到缓冲区中);
头文件:
原型:
char *fgets(char *s, int size, FILE *stream);
参数:
char *s:保存获取到的字符串;
int size:从文件中读取 size-1 个字节,因为要保留'\0'的位置;
注意:如果一行不足size-1个字符,则读取个数为一行的实际个数。
FILE *stream;
返回值:
成功,返回char *s;
失败,返回NULL;更新errno;
注意:
1.读取到文件末尾时,返回NULL
2.没有遇到\n,但长度达到size-1也会默认为一行,此时应判断是否读取了一行(判断结束符的前一位是否是换行符)即:buf[strlen(buf)-1]==’\n’
例子
char buf[20] = "";
fp = fopen("./fgets.txt", "r");
fgets(buf, 20-1, fp);
printf("%s\n",buf);
小练习
1.写一个函数,计算一个文件中共有几行。 用fgets实现
提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符)
2.用fgets和fputs实现文件的拷贝,将a.c的内容拷贝到b.c中
第1题
1.写一个函数,计算一个文件中共有几行。 用fgets实现
提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符);
#include <stdio.h>
#include <string.h>
int file_line(FILE* fp)
{
int line = 0;
char s[10] = "";
//fgets读取到末尾时返回NULL
while(fgets(s, 10-1, fp) != NULL)
{
int i = 0;
//判断当达到size-1长度时是否是完整的一行,若是则行数加1
//方法1:
for(i=0; i<strlen(s); i++)
{
if('\n' == s[i])
{
line++;
}
}
//方法2:
if(s[strlen(s)-1]=='\n')
{
line++;
}
bzero(s, strlen(s));
}
return line;
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
return -1;
}
FILE* fp = fopen(argv[1], "r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
int line = file_line(fp);
printf("line = %d\n", line);
fclose(fp);
return 0;
}
第2题
2.用fgets和fputs实现文件的拷贝,将a.c的内容拷贝到b.c中;
提示:在没有读到文件末尾(返回值是否为NULL)的情况下,读一行写一行
#include <stdio.h>
void file_copy(FILE* rp, FILE* wp)
{
char str[10] = "";
//在while中已经拿出判断了,所以只需要写即可,不用再拿出来
while(fgets(str, 10-1, rp) != NULL)
{
fputs(str, wp);
bzero(str, sizeof(str));
}
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
return -1;
}
FILE* fp_r = fopen(argv[1], "r");
if(NULL == fp_r)
{
perror("fopen");
return -1;
}
FILE* fp_w = fopen("./b.c", "w");
file_copy(fp_r, fp_w);
fclose(fp_r);
fclose(fp_w);
return 0;
}
11)fwrite
功能:将数据以二进制的格式输出到文件中;
头文件:
#include <stdio.h>
原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
void *ptr:要输出的数据的地址;可以是任意类型数据;
size_t size:以size个字节为单位输出;例如
如果要输出的是int类型数据,size == 4;
如果要输出的是char类型数据,size == 1;
如果要输出的是自定义结构体 struct aa,size == sizeof(struct aa);
size_t nmemb:要输出的数据个数;
FILE *stream:流指针;
返回值:
成功,返回输出的数据个数,其实就是nmemb;
失败,返回-1,更新errno;
例子
FILE* fp = fopen("./fwrite.txt", "w");
if(NULL == fp)
{
perror("fopen");
return -1;
}
//fwrite写入数据
int arr[4] = {1,2,3,4};
if(fwrite(arr, 4, 4, fp)<0)
{
perror("fwrite");
return -1;
}
printf("写入成功\n");
fclose(fp);
12)fread
功能:从文件中获取二进制数据;
头文件:
#include <stdio.h>
原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
void *ptr:成功读取数据后,数据存储的位置;
size_t size:以size个字节为单位输入;例如
如果要输入的是int类型数据,size == 4;
如果要输入的是char类型数据,size == 1;
如果要输入的是自定义结构体 struct aa,size == sizeof(struct aa);
size_t nmemb:要输入的数据个数;
FILE *stream:流指针;
返回值:
成功,返回读取到的个数nmemb;
失败,返回-1,更新errno;
注意:
1.比如要读的是文本文件,则可以指定按字符读写,如果是数据文件,则可以指定以整型数为对象读写
2.成功返回实际读写的个数,出错时返回EOF,如果指定大小大于缓冲区大小,不会报错,会将缓冲区的个数读出并返回
3.读到文件末尾时返回0
例子
fp = fopen("./fwrite.txt", "r");
int brr[4] = {0};
fread(brr, 4, 4, fp);
int i =0;
for(i=0; i<4; i++)
{
printf("%d ",brr[i]); //打印 1 2 3 4
}
putchar(10);
fclose(fp);
return 0;
}
13)fseek
功能:偏移文件指针;
头文件:
#include <stdio.h>
原型:
int fseek(FILE *stream, long offset, int whence);
参数:
FILE *stream:流指针;
long offset:相对于whence的偏移量
int whence:
SEEK_SET 文件开头
SEEK_CUR 当前位置
SEEK_END 文件结尾
返回值:
成功,返回0;
失败,返回-1,更新errno;
void rewind(FILE *stream); 将文件指针偏移到开头;
rewind(fp) 相当于 fseek(fp, 0, SEEK_SET);
注意:
1.fseek和lseek不可以用于无名管道
2.获取文件大小方法A:用fseek和ftell
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
获取文件大小方法B:用lseek
int c=lseek(fp);
14)ftell
功能:获取文件指针当前位置到文件开头的偏移量;
头文件:
#include <stdio.h>
原型:
long ftell(FILE *stream);
参数:
FILE *stream:流指针;
返回值:
成功,返回非负数;
失败,返回-1;
15)time
功能:获取从1970-01-01到至今的秒数;
头文件:
#include <time.h>
原型:
time_t time(time_t *t);
参数:
time_t *t:存储获取到的时间; time_t : long int
返回值:
成功,返回获取到的时间;
失败,返回(time_t) -1,更新errno;
//方法1
time_t t = time(NULL);
//方法2
time_t t1;
time(&t1);
16)localtime
功能:将1970-01-01到至今的秒数,转换成日历格式;
头文件:
#include <time.h>
原型:
struct tm *localtime(const time_t *timep);
参数:
time_t *timep:需要转换的秒数的地址;
返回值:
成功,返回存储日历的结构体地址。struct tm *
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */ 秒
int tm_min; /* Minutes. [0-59] */ 分
int tm_hour; /* Hours. [0-23] */ 时
int tm_mday; /* Day. [1-31] */ 日
int tm_mon; /* Month. [0-11] */ 月 = tm_mon+1
int tm_year; /* Year - 1900. */ 年 = tm_year+1900
int tm_wday; /* Day of week. [0-6] */ 星期
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
}
失败,返回NULL,更新errno;
例子
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
//方法1
while(1)
{
time_t t = time(NULL);
//方法2
time_t t1;
time(&t1);
// printf("%ld %ld\n", t, t1);
struct tm* info = localtime(&t);
if(NULL == info)
{
perror("localtime");
return -1;
}
// \r为在控制台同一行覆盖刷新输出
printf("%d-%02d-%02d %02d-%02d-%02d\r",info->tm_year+1900,\
info->tm_mon+1, info->tm_mday, info->tm_hour, \
info->tm_min, info->tm_sec);
fflush(stdout);
sleep(1);
}
return 0;
}
小练习
1.获取当前的时间,以日历的形式输出到time.txt中。要求如下:
1) 格式:[行号] 时间
[0] 星期3 2020-12-21 16-58-06
[1] 星期3 2020-12-21 16-58-07
[2] 星期3 2020-12-21 16-59-00
2)程序结束后,再次运行程序,保证编号从上次的编号开始
#include <stdio.h>
#include <time.h>
//获取文件行数
int file_line(FILE* fp)
{
int line = 0;
int c = 0;
while((c = fgetc(fp)) != EOF)
{
if('\n' == c)
{
line++;
}
}
return line;
}
int main(int argc, const char *argv[])
{
FILE* fp = fopen("./time.txt", "a+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
int line = file_line(fp);
//循环获取时间
time_t t = 0;
struct tm* info = NULL;
while(1)
{
line++;
t = 0;
info = NULL;
t = time(NULL);
info = localtime(&t);
if(NULL == info)
{
perror("localtime");
return -1;
}
fprintf(fp,"[%02d] 星期%d %d-%02d-%2d %02d-%02d-%02d\n",line, info->tm_wday, info->tm_year+1900, info->tm_mon+1, info->tm_mday,info->tm_hour, info->tm_min, info->tm_sec);
fflush(fp);
sleep(1);
}
fclose(fp);
return 0;
}
17)sprintf
功能:将数据格式化输出到字符串中;可做数据拼接。
头文件:
#include <stdio.h>
原型:
int sprintf(char *str, const char *format, ...);
参数:
char *str:将数据输出到该字符串中;
char *format:格式化输出;
...:不定参数;
返回值:
成功,返回格式化输出的数据个数;
失败,返回负数;
例子
int main(int argc, const char *argv[])
{
int a = 10;
char str[10] = "hello"; //"hello10"
char str1[10] = "";
char buf[15] = "";
printf("%s%04d\n", str, a);
sprintf(buf, "%s %04d",str, a);
printf("%s\n",buf);
int b = 0;
sscanf(buf, "%s %d",str1, &b);
printf("str1=%s, b=%d\n", str1, b);
return 0;
}
18)sscanf
功能:从字符串中以标准格式输入数据
头文件:
#include <stdio.h>
原型:
int sscanf(const char *str, const char *format, ...);
参数:
const char *str:从这个字符串中获取数据
const char *format :格式化输入
...:格式化输出
返回值:
成功,返回成功赋值的变量个数。
为0,成功赋值的个数为0;
失败,返回-1,更新errno
例子
#include <stdio.h>
int main(int argc, const char *argv[])
{
char str[20] = "hello world 123";
char s1[10] = "";
char s2[10] = "";
int a = 0;
sscanf(str, "%s %s %d", s1, s2, &a);
printf("%s %s %d\n",s1, s2, a);
return 0;
}
sscanf的格式化:
%c : 获取一个字符
%*c : 抑制一个字符的接收。
%[^\n]: 获取内容,直到\n为止,不包括'\n'
%[^,]: 获取内容,直到遇到‘,’为止,不包括','
%*[^\n]:抑制字符的吸收,直到遇到\n为止。
%*[^,]: 抑制字符的吸收,直到遇到','为止,','依然存在;
例子
int main(int argc, const char *argv[])
{
//hello,world
char s1[20]="", s2[10]="";
sscanf("hello,world","%[^,]%*c%s",s1,s2);
printf("%s %s\n",s1, s2); //hello world
return 0;
}
3. 文件IO
1. 文件IO是不带缓冲区的
2. 文件IO函数是由操作系统提供的,与操作系统绑定,又称之为系统调用
3. 文件IO是通过文件描述符来维护一个文件。
【1】文件描述符
1.概念
1. 尝试打开一个文件的时候,系统会主动给这个文件分配一个编号(文件描述符),用这个编号来描述一个文件。
2. 标准IO是对文件IO的二次封装,在文件IO的基础上封装了一个缓冲区,同时将文件描述符也一起封装到了FILE结构体中。
3. 文件IO对文件的读写,是通过文件描述符实现的。
4. 标准IO对文件的读写,是通过操作流指针实现的。
2.特殊的文件描述符
特殊的流指针 | 文件描述符 | 宏 | |
---|---|---|---|
stdin | stdin->_fileno | 0 | STDIN_FILENO |
stdout | stdout->_fileno | 1 | |
stderr | stderr->_fileno | 2 |
3.文件描述符的总量
文件描述符的编号:[0,1023] ,总共1024个。
其中0,1,2分别对应stdin, stdout, stderr.
注意:
1.每个进程能打开的最大文件描述符1023,数量是1024
2.文件描述符的资源是有限的,在不使用的情况下,需要关闭;
思考:如何验证文件描述符的总量,以及最大值。
提示:循环打开文件,不关闭
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("%d %d %d\n", stdin->_fileno, stdout->_fileno, stderr
FILE* fp = NULL;
while(1)
{
fp = fopen("fileno.txt", "w");
if(NULL == fp)
{
perror("fopen");
break;
}
printf("%d ", fp->_fileno);
}
putchar(10);
return 0;
}
getdtablesize()函数
功能:获取当前进程能打开的文件描述符总量;
头文件:
#include <unistd.h>
原型:
int getdtablesize(void);
返回值:
返回当前进程能打开的文件描述符总量;
例子:
int size = getdtablesize();
printf("size = %d\n", size); //1024
【2】文件IO函数
1.常见的文件IO函数
man 2 卷
open / close 打开关闭一个文件
read / write 读写一个文件
lseek 偏移
2.文件IO函数的使用
1)open
功能:打开一个文件,返回文件描述符;
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数:
char *pathname:要打开的文件路径+文件名;
int flags:文件打开方式;如果要包含多种方式,可以用按位或(|)的方式连接 O_WRONLY|O_CREAT|O_TRUNC
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
----以上三个必须包含一种----------
O_APPEND:追加的方式打开
O_CREAT:文件不存在则创建文件
O_TRUNC:清空文件;
mode_t mode:文件的八进制权限,0777 0664;
如果 int flags包含了 O_CREAT ;必须要有mode参数,否则会忽略 O_CREAT
S_IRWXU 00700
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
--------------以上是当前用户的权限--------------------
S_IRWXG 00070
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
0777 = S_IRWXU|S_IRWXG|S_IRWXO;
返回值:
成功,返回文件描述符;
失败,返回-1,更新errno;
int creat(const char *pathname, mode_t mode); //创建文件;
creat("./text.txt", 0777);
相当于 open("./text.txt", O_RDWR|O_TRUNC|O_CREAT, 0777);
2)umask
The permissions of the created file are (mode & ~umask):文件创建的真实权限是 mode & ~umask;
1.什么是umask
umask:文件权限掩码,会影响文件创建时候的权限。文件创建的真实权限是 mode & ~umask;
2.如何查看umask
$ umask 得到结果 0002;
实际创建的文件权限:
0777 & ~0002 = 111 111 111 & ~000 000 010 = 111 111 111 & 111 111 101 = 111 111 101 = 0775
3.设置umask
方法1:在终端上设置,但是只在当前终端有效
$ umask 0000 //将umask清零
方法2:通过umask()函数设置
头文件:
#include <sys/types.h>
#include <sys/stat.h>
原型:
mode_t umask(mode_t mask);
例子:
umask(0);
例子
umask(0);
int fd = open("./open.c", O_WRONLY|O_CREAT|O_TRUNC, 0777);
if(-1 == fd)
{
perror("open");
return -1;
}
printf("打开成功\n");
close(fd);
3)close
功能:关闭一个文件。
头文件:
#include <unistd.h>
原型:
int close(int fd);
参数:
int fd:要关闭的文件描述符;
返回值:
成功,返回0;
失败,返回-1;更新errno
思考:标准IO中的 r r+ w w+ a a+ 分别对应文件IO中的O_XXX的那些组合
fopen | open |
---|---|
r | O_RDONLY |
r+ | O_RDWR |
w | O_WRONLY|O_CREAT|O_TRUNC |
w+ | O_RDWR|O_CREAT|O_TRUNC |
a | O_WRONLY|O_CREAT|O_APPEND |
a+ | O_RDWR|O_CREAT|O_APPEND |
4)write
功能:将数据写入到文件中;
头文件:
#include <unistd.h>
原型
ssize_t write(int fd, const void *buf, size_t count);
参数:
int fd:文件描述符;
void *buf:要写入文件中的数据首地址,可以是任意类型;
size_t count:要写入的数据大小,以字节为单位;
返回值:
成功,返回写入的数据个数;
失败,返回-1,更新errno;
例子
int main(int argc, const char *argv[])
{
umask(0); //清空umask
int fd = open("write.c", O_RDWR|O_CREAT|O_TRUNC, 0777);
if(-1 == fd)
{
perror("open");
return -1;
}
char str[20] = "hello world";
int res = write(fd, str, strlen(str));
if(res < 0)
{
perror("write");
close(fd);
return -1;
}
printf("成功写入%d个字符\n",res);
int arr[3] = {1,2,3};
res = write(fd, arr, sizeof(arr));
if(res < 0)
{
perror("write");
close(fd);
return -1;
}
printf("成功写入%d个字符\n",res);
//偏移文件指针
int size = lseek(fd, 0, SEEK_SET);
bzero(str, sizeof(str));
bzero(arr, sizeof(arr));
res = read(fd,str, 11);
if(res < 0)
{
perror("read");
close(fd);
return -1;
}
printf("%d %s\n", res, str);
res = read(fd, arr, 12);
printf("%d %d %d %d\n",res, arr[0],arr[1], arr[2]);
//将文件指针偏移到结尾
size = lseek(fd, 0, SEEK_END);
printf("size=%d\n",size);
close(fd);
return 0;
}
5)read
功能:从文件中读取数据;
头文件:
#include <unistd.h>
原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
int fd:文件描述符;
void *buf:存储读取到的数据;
size_t count:要读取的数据大小,以字节为单位;
返回值:
成功,返回读取到的数据个数;
返回0,表示读到文件结尾。
失败,返回-1,更新errno;
注意:
1.返回0,表示读到文件结尾
6)lseek
功能:偏移文件指针;
头文件:
#include <sys/types.h>
#include <unistd.h>
原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
int fd:文件描述符;
off_t offset:相对于int whence的偏移量;
int whence:
SEEK_SET 文件开头
SEEK_CUR 当前位置;
SEEK_END 文件结尾;
返回值:
成功,返回设置后的文件指针相对于文件开头的偏移量;
失败,返回-1,更新errno;
计算文件大小:
size = lseek(fd, 0, SEEK_END);
printf("size=%d\n",size);
相当于
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
小练习:
1.通过write read函数实现图片的拷贝;
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
int fd_r = -1, fd_w = -1;
if((fd_r = open("./1.png", O_RDONLY))<0)
{
fprintf(stderr, "line:%d", __LINE__);
perror("open");
return -1;
}
if((fd_w = open("./2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664))<0)
{
fprintf(stderr, "line:%d", __LINE__);
perror("open");
return-1;
}
char c = 0;
while(1)
{
int ret = read(fd_r, &c, 1);
if(ret < 0)
{
perror("read");
break;
}
else if(!ret)
{
printf("拷贝结束\n");
break;
}
ret = write(fd_w, &c, 1);
if(ret < 0)
{
perror("write");
close(fd_r);
close(fd_w);
return -1;
}
}
close(fd_r);
close(fd_w);
return 0;
}
3.文件属性相关的函数
1)stat
功能:获取文件属性;
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
原型:
int stat(const char *path, struct stat *buf);
参数:
char *path:指定文件的路径+名字;
struct stat *buf:系统定义的结构体,存储获取到的文件属性;
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */ inode号
mode_t st_mode; /* protection */ 文件类型+权限
nlink_t st_nlink; /* number of hard links */ 硬链接数
uid_t st_uid; /* user ID of owner */ 当前用户的uid
gid_t st_gid; /* group ID of owner */ 组的gid
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */ 文件大小
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
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 */ 最后一次被改变状态的时间
};
返回值:
成功,返回0;
失败,返回-1,更新errno;
例子
struct stat buf;
int ret = stat("./1_fileno.c", &buf);
if(ret < 0)
{
perror("stat");
return -1;
}
printf("获取属性成功\n");
//inode号
printf("inode:%ld\n", buf.st_ino);
//文件类型+权限
printf("mode: %o\n", buf.st_mode);
//硬链接数
printf("link: %d\n", buf.st_nlink);
2)提取文件权限
mode_t st_mode; /* protection */ 文件类型+权限
st_mode:是一个32位无符号的整形变量;其中低9位([0, 8]),代表了文件的权限(rwx rwx rwx)
用st_mode & 相应的权限八进制数,如果结果大于0,说明有该权限,如果结果为0,则没有该权限。
st_mode: 100664
664---> 110 110 100
& 100 000 000 0400
--------------------
100 000 000 -->0400 > 0 说明当前用户有可读权限
664---> 110 110 100
& 001 000 000 0100
--------------------
000 000 000 == 0 当前用户没有可执行权限
对应权限的宏:
S_IRWXU 00700 mask for file owner permissions
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 mask for group permissions
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 mask for permissions for others (not in group)
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
例子
//文件权限
getFileType(buf.st_mode);
//获取文件权限方法1
#if 0
void getPermission(mode_t mode)
{
if(mode & 0400)
putchar('r');
else
putchar('-');
if(mode & 0200)
putchar('w');
else
putchar('-');
if(mode & 0100)
putchar('x');
else
putchar('-');
if(mode & 0040)
putchar('r');
else
putchar('-');
if(mode & 0020)
putchar('w');
else
putchar('-');
if(mode & 0010)
putchar('x');
else
putchar('-');
if(mode & 0004)
putchar('r');
else
putchar('-');
if(mode & 0002)
putchar('w');
else
putchar('-');
if(mode & 0001)
putchar('x');
else
putchar('-');
putchar(10);
}
#endif
//获取文件权限方法二:
void getPermission(mode_t mode)
{
int i =0;
for(i=8; i>=0; i--)
{
if((mode & (1<<i)) == 0) //000 000 001 << 8 = 100 000 000
{
putchar('-');
continue;
}
switch(i%3)
{
case 2:
putchar('r');
break;
case 1:
putchar('w');
break;
case 0:
putchar('x');
break;
}
}
putchar(10);
}
3)提取文件类型
文件类型:7种 bsp-lcd
i)通过宏函数判断
S_ISREG(m) is it a regular file? 判断是否是普通文件
S_ISDIR(m) directory? 判断是否是目录文件
S_ISCHR(m) character device? c
S_ISBLK(m) block device? b
S_ISFIFO(m) FIFO (named pipe)? p
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.) l
S_ISSOCK(m) socket? (Not in POSIX.1-1996.) s
参数:
m:mode_t st_mode;
返回值:
1,代表是该类型文件;
0,代表不是该类型文件;
例子
//获取文件类型 方法1:
void getFileType(mode_t m)
{
if(S_ISREG(m))
putchar('-');
else if(S_ISDIR(m))
putchar('d');
else if(S_ISCHR(m))
putchar('c');
else if(S_ISBLK(m))
putchar('b');
else if(S_ISFIFO(m))
putchar('p');
else if(S_ISLNK(m))
putchar('l');
else if(S_ISSOCK(m))
putchar('s');
}
ii)通过提取文件类型
mode_t st_mode; /* protection */ 文件类型+权限
st_mode:其中[低16位, 低13位]共4bit, 代表文件类型;
S_IFMT 0170000 bit mask for the file type bit fields
001 111
st_mode & S_IFMT :实现提取[低16位, 低13位]共4bit,这4bit就代表了文件类型。
st_mode: 100664
100664 --> 001 000 000 110 110 100
S_IFMT 170000 --> 001 111 000 000 000 000
---------------------------------------------
001 000 ... ... ... ... 提取出4bit: 001 000;
将结果与下列的宏做比较,如果相等,那么就是对应的文件类型:
S_IFSOCK 0140000 socket s
S_IFLNK 0120000 symbolic link l
S_IFREG 0100000 regular file -
S_IFBLK 0060000 block device b
S_IFDIR 0040000 directory d
S_IFCHR 0020000 character device c
S_IFIFO 0010000 FIFO p
例子
//获取文件类型 方法2:
void getFileType(mode_t m)
{
mode_t type = m & S_IFMT;
switch(type)
{
case S_IFSOCK:
putchar('s');
break;
case S_IFLNK:
putchar('l');
break;
case S_IFREG:
putchar('-');
break;
case S_IFBLK:
putchar('b');
break;
case S_IFDIR:
putchar('d');
break;
case S_IFCHR:
putchar('c');
break;
case S_IFIFO:
putchar('p');
break;
}
}
4)获取文件所属用户
uid_t st_uid; /* user ID of owner */ 当前用户的uid
getpwuid
功能:通过 struct stat 结构体中的 uid_t st_uid 成员变量,获取文件所述用户;
头文件:
#include <sys/types.h>
#include <pwd.h>
原型:
struct passwd *getpwuid(uid_t uid);
参数:
uid_t uid:文件所属用户的uid;
返回值:
成功,返回存储文件所属用户信息的结构体;
失败,NULL,更新errno;
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 */
};
例子
//文件所属用户名;
char usr_name[20] = "";
getUsrName(usr_name , buf.st_uid);
printf("%s\n", usr_name);
//获取文件所属用户的名字
char* getUsrName(char* usr_name, uid_t uid)
{
struct passwd *pwd = NULL;
pwd = getpwuid(uid);
if(NULL == pwd)
{
perror("getpwuid");
return NULL;
}
strcpy(usr_name, pwd->pw_name);
return usr_name;
}
5)获取文件所属用户组
gid_t st_gid; /* group ID of owner */ 组的gid
getgrgid
功能:通过 struct stat 结构体中的 gid_t st_gid 成员变量,获取文件所述用户组;
头文件:
#include <sys/types.h>
#include <grp.h>
原型:
struct group *getgrgid(gid_t gid);
参数:
gid_t gid:
返回值:
成功,返回存储组用户信息的结构体指针 struct group *
失败,返回NULL;更新errno;
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
例子
//获取文件所属组用户的名字
char* getGrpName(char* grp_name, gid_t gid)
{
struct group *grp = getgrgid(gid);
if(NULL == grp)
{
perror("getgtgid");
return NULL;
}
strcpy(grp_name, grp->gr_name);
return grp_name;
}
//文件所属组用户的名字;
char grp_name[20] = "";
getGrpName(grp_name, buf.st_gid);
printf("%s\n", grp_name);
小练习
读取一个文件的信息,main函数外部传参的方式。要求输出结果格式如下;
-rw-rw-r-- 1 linux linux 377 10 22 10:46 1_fileno.c
4.操作目录的函数
1)opendir
功能:根据目录里名打开一个目录;
头文件:
#include <sys/types.h>
#include <dirent.h>
原型:
DIR *opendir(const char *name);
参数:
char *name:指定要打开的目录的路径+名字;
返回值:
成功,返回 DIR * 目录流指针;
失败,返回NULL;
例子
int main(int argc, const char *argv[])
{
DIR* dp = opendir("./");
if(NULL == dp)
{
perror("opendir");
return -1;
}
printf("打开目录成功\n");
closedir(dp);
return 0;
}
2)closedir
功能:关闭目录;
头文件:
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
原型:
int closedir(DIR *dirp);
参数:
DIR *dirp:目录流指针;
返回值:
成功,返回0;
失败,返回-1;
3)readdir
功能:通过DIR*目录流指针,读取目录;
头文件:
#include <dirent.h>
原型:
struct dirent *readdir(DIR *dirp);
参数:
DIR *dirp:目录流指针;
返回值:
成功,返回存储目录信息的结构体地址, struct dirent *;
失败,返回NULL;更新errno;
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */ 目录中文件名字
};
注意:
1.读到文件末尾返回NULL
例子
int main(int argc, const char *argv[])
{
DIR* dp = opendir("./");
if(NULL == dp)
{
perror("opendir");
return -1;
}
printf("打开目录成功\n");
//读取目录中的数据
struct dirent *dir_read = NULL;
while(1)
{
dir_read = readdir(dp);
if(NULL == dir_read)
{
perror("readdir");
fprintf(stderr, "目录已读取完毕\n");
break;
}
printf("%s\n", dir_read->d_name);
}
closedir(dp);
return 0;
}
小练习
1.代码实现,打开目录,显示该目录下面所有文件的属性,权限,最有一次被修改的时间,类似ls –l。例子:
-rw-rw-r-- 1 linux linux 377 10 22 10:46 1_fileno.c
drwxrwxr-x 5 linux linux 4096 10 21 01:29:47 2020 .
drwxrwxr-x 2 linux linux 4096 9 20 23:25:06 2020 file1
-rw-rw-r-- 1 linux linux 21 8 21 01:29:47 2020 t1.c
drwxrwxr-x 2 linux linux 4096 7 20 23:25:06 2020 file3
drwxrwxr-x 2 linux linux 4096 4 20 23:25:06 2020 file2
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>
//获取文件权限
void getPermission(mode_t mode)
{
int i =0;
for(i=8; i>=0; i--)
{
if((mode & (1<<i)) == 0) //000 000 001 << 8 = 100 000 000
{
putchar('-');
continue;
}
switch(i%3)
{
case 2:
putchar('r');
break;
case 1:
putchar('w');
break;
case 0:
putchar('x');
break;
}
}
}
//获取文件类型
void getFileType(mode_t m)
{
mode_t type = m & S_IFMT;
switch(type)
{
case S_IFSOCK:
putchar('s');
break;
case S_IFLNK:
putchar('l');
break;
case S_IFREG:
putchar('-');
break;
case S_IFBLK:
putchar('b');
break;
case S_IFDIR:
putchar('d');
break;
case S_IFCHR:
putchar('c');
break;
case S_IFIFO:
putchar('p');
break;
}
}
//获取文件所属用户的名字
char* getUsrName(char* usr_name, uid_t uid)
{
struct passwd *pwd = NULL;
pwd = getpwuid(uid);
if(NULL == pwd)
{
perror("getpwuid");
return NULL;
}
strcpy(usr_name, pwd->pw_name);
return usr_name;
}
//获取文件所属组用户的名字
char* getGrpName(char* grp_name, gid_t gid)
{
struct group *grp = getgrgid(gid);
if(NULL == grp)
{
perror("getgtgid");
return NULL;
}
strcpy(grp_name, grp->gr_name);
return grp_name;
}
//获取时间
char* getTime(char* time, time_t t)
{
struct tm* info = localtime(&t);
sprintf(time,"%02d %2d %02d:%02d", info->tm_mon+1, info->tm_mday,info->tm_hour, info->tm_min);
return time;
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
fprintf(stderr, "请输入文件路径+文件名\n");
return -1;
}
DIR* dp = opendir(argv[1]);
if(NULL == dp)
{
perror("opendir");
return -1;
}
struct dirent* rp = NULL;
struct stat buf;
char path[30] = "";
int size = 0;
char time[30] = "";
while(1)
{
rp = readdir(dp);
if(NULL == rp)
{
break;
}
bzero(path, sizeof(path));
sprintf(path, "%s%s", argv[1], rp->d_name);
// printf("path = %s\n", path);
int ret = stat(path, &buf);
if(ret < 0)
{
perror("stat");
return -1;
}
//文件类型+权限
getFileType(buf.st_mode);
getPermission(buf.st_mode);
//硬链接数
printf(" %2d", buf.st_nlink);
//文件所属用户名;
char usr_name[20] = "";
getUsrName(usr_name , buf.st_uid);
printf("%7s", usr_name);
//文件所属组用户的名字;
char grp_name[20] = "";
getGrpName(grp_name, buf.st_gid);
printf("%7s", grp_name);
//文件大小
size = buf.st_size;
printf("%7d",size);
//时间
bzero(time, strlen(time));
printf("%14s", getTime(time, buf.st_mtime));
//名字
printf("%10s\n", rp->d_name);
}
return 0;
}