第一类:标准IO(高级磁盘IO)
第二类:文件IO(低级磁盘IO)
注意:
库函数和系统调用函数的关系:大多数库函数都是由系统调用函数封装起来的。
文件被打开的时候,会默认具备3个类:
因此:当用户以后想要操作文件时,就需要先打开文件,此时会拥有一个文件流指针,以后通过该文件指针就可以操作文件。
(1)缓冲区被放满:
#include
#include
int main(int argc, const char *argv[])
{
while(1)
{
printf("hello");
//延时函数
sleep(1);
}
return 0;
}
此时处于不断向缓冲区放hello,延时时间为1秒钟1次,因此当缓冲区没有放满时,不会刷新缓冲区,也就看不到hello。
(2)程序结束:
#include
#include
int main(int argc, const char *argv[])
{
printf("hello");
return 0;
}
(3)强制刷新:
#include
#include
int main(int argc, const char *argv[])
{
while(1)
{
printf("hello");
sleep(1);
//强制刷新
fflush(stdout);
}
return 0;
}
一秒钟后再强制刷新输出流
提供给用户来操作文件的方式有3种:
不管是哪种方式来操作文件,流程为:
(1)打开文件FILE *fopen(const char *path,const char *mode);
#include
int main(int argc, const char *argv[])
{
//打开一个指定文件
//char *FileName = "1.c";
//char *Mode = "r";
FILE *fr = fopen(argv[1],"r");
//FILE *fr = fopen("2.c","r");
//FILE *fr = fopen(FileName, Mode);
//判断fr的值
if(NULL == fr)
{
perror("fopen error");
return -1;
}
printf("fopen_read ok!\n");
//关闭文件
fclose(fr);
return 0;
}
(2)操作文件:读写文件
3种方法:按照字符操作、按照行操作、按照块操作
(3)关闭文件
头文件:#include
函数原型:int fclose(FILE *stream);
因此:打开文件和关闭文件在标准IO下都是使用同一个接口,只是在操作文件的方式上存在选择.
(1)函数名:fgetc()
fputc
(2)头文件:#include
(3)函数原型:
int fgetc(FILE *stream);
int fputc(int c, FILE*stream);
案例:
实现对于指定一个文件读取,读取方式为按照字符操作,将读取完毕之后的结果显示在终端上。(自己实现命令cat 文件名)
思路:
1、打开需要显示文件内容所在的文件
2、获取一个字符
3、输出一个字符
4、重复2、3直至文件末尾截止
5、关闭文件
代码:
#include
//功能:实现命令cat的功能
int main(int argc, const char *argv[])
{
//入参检查
if(argc < 2)
{
printf("传参过少!\n");
return -1;
}
//打开需要读取的文件
FILE * fr = fopen(argv[1], "r");
if(NULL == fr)
{
perror("fopen_read error");
}
printf("fopen_read success!\n");
//借助于循环完成读取并显示
while(1)
{
//读取
char chr = fgetc(fr);
//判断--》什么时候结束while循环
/*if(EOF == chr)
{
printf("文件读取完毕!\n");
break;
}
*/
if(feof(fr))//任何类型的文件都可判断是否到达文件末尾
{
//抵达文件末尾
printf("到头了!\n");
break;
}
//显示
char ret = fputc(chr, stdout);//putchar()
}
//关闭文件
fclose(fr);
return 0;
}
(1)标志:寻找’\n’
(2)函数: fgets
fputs
(3)函数原型:char * fgets(char *s, int size, FILE *stream);
(4)思考:fgets什么时候会返回?
情况1:第一次遇到’\n’就会返回
情况2:该行会比较长,需要多次读取才能读完,所以位于读到’\n’之前的多次都是读取到size-1就返回。
(5)案例:
实现wc -l测试文件行数的功能
#include
#include
//功能:实现wc -l 命令统计文件行数的功能
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("error!\n");
return -1;
}
FILE *fr = fopen(argv[1], "r");
if(NULL == fr)
{
perror("fopen_read error");
return -1;
}
printf("fopen_read ok!\n");
int line = 0;
char Buf[20] = {0};
while(1)
{
if(NULL == fgets(Buf, sizeof(Buf), fr))
{
break;
}
//判断
if( '\n' == Buf[strlen(Buf)-1] )
{
line++;
}
}
printf("%d %s\n",line, argv[1]);
fclose(fr);
return 0;
}
(1)函数:fread()
fwrite()
(2)函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
(3)案例:
保存学生信息:
#include
#include
enum VALUE
{
FOPEN_ERROR = -4,
MALLOC_ERROR = -3,
NULL_ERROR,
ERROR,
OK,
};
//操作一块空间
typedef struct student
{
char Name[20];
int Age;
float Height;
}Stu;
int SaveStuMessage_Func(Stu *pStu)
{
if(NULL == pStu)
{
return NULL_ERROR;
}
//打开文件
FILE *fw = fopen("Message.txt", "w");
if(NULL == fw)
{
perror("fopen error");
return FOPEN_ERROR;
}
Stu *pStudent = pStu;
fwrite(pStudent, 1, sizeof(Stu), fw);
fclose(fw);
return OK;
}
int main(int argc, const char *argv[])
{
//自己输入该学生的信息
printf("请输入该学生的信息:\n");
Stu *pStu = (Stu *)malloc(sizeof(Stu));
if(NULL == pStu)
{
printf("malloc_error!\n");
return ERROR;
}
scanf("%s%d%f",pStu->Name, &pStu->Age, &pStu->Height);
printf("保存学生信息之前:姓名:%s\t年龄:%d\t身高:%.1f\n", \
pStu->Name, pStu->Age, pStu->Height);
//保存学生信息到文件中
int ret = SaveStuMessage_Func(pStu);
if(ret < 0)
{
return ERROR;
}
else
{
printf("保存学生信息完毕!\n");
}
return OK;
}
读取学生信息到结构体空间:
#include
#include
enum VALUE
{
FOPEN_ERROR = -4,
MALLOC_ERROR = -3,
NULL_ERROR,
ERROR,
OK,
};
//操作一块空间
typedef struct student
{
char Name[20];
int Age;
float Height;
}Stu;
int LoadStuMessage_Func(Stu *pStu)
{
if(NULL == pStu)
{
return NULL_ERROR;
}
//打开文件
FILE *fr = fopen("Message.txt", "r");
if(NULL == fr)
{
perror("fopen error");
return FOPEN_ERROR;
}
Stu *pStudent = pStu;
fread(pStudent, 1, sizeof(Stu), fr);
fclose(fr);
return OK;
}
int main(int argc, const char *argv[])
{
//申请学生的空间
Stu *pStu = (Stu *)malloc(sizeof(Stu));
if(NULL == pStu)
{
printf("malloc_error!\n");
return ERROR;
}
//导入信息到结构体空间
int ret = LoadStuMessage_Func(pStu);
if(ret < 0)
{
return ERROR;
}
else
{
printf("导入的学生信息打印为:姓名:%s\t年龄:%d\t身高:%.1f\n", \
pStu->Name, pStu->Age, pStu->Height);
}
return OK;
}
fseek()/ftell()/rewind()
函数原型:
(1)头文件:#include
(2)函数原型:
int fseek(FILE * stream,long offset,intwhence);
long ftell(FILE * stream);
void rewind(FILE * stream);
代码如下:
#include
int main(int argc, const char *argv[])
{
//FILE *fd = fopen(argv[1], "w");
FILE *fd = fopen(argv[1], "a");
if(NULL == fd)
{
return -1;
}
long size = ftell(fd);
printf("目前:文件指示位置的大小为%ld个字节\n",size);
//偏移到文件开头位置
//fseek(fd, 0, SEEK_SET);
rewind(fd); //等价于fseek(fd, 0, SEEK_SET)
size = ftell(fd);
printf("目前:文件指示位置的大小为%ld个字节\n",size);
fclose(fd);
return 0;
}
open close read write
头文件:
#include
#include
#include
函数原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
头文件:#include
函数原型:int close(int fd);
头文件:#include
函数原型:ssize_t write(int fd, const void *buf, size_t count);
(1)头文件:#include
(2)函数原型:ssize_t read(int fd, void *buf, size_t count);
(3)案例:对于同一个文件进行读写操作的代码如下:
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
//打开文件
int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0664);
if(fd < 0)
{
perror("open error");
return -1;
}
printf("open ok!\n");
//读写操作
//定义存储即将写入文件内容的空间
char Buf[100] = {0};
printf("请输入文本:\n");
fgets(Buf, sizeof(Buf), stdin);
int wr_count = write(fd, Buf, sizeof(Buf));
//int wr_count = write(fd, Buf, strlen(Buf));
if(wr_count < 0)
{
perror("write error");
close(fd);
return -1;
}
else if(0 == wr_count)
{
printf("未写入内容!\n");
close(fd);
return -1;
}
else
{
printf("写入文件成功,写入的字符个数为:%d\n",wr_count);
}
//恢复文件指示位置至文件开头
lseek(fd, 0, SEEK_SET);
//读取文件内容
//定义存储从文件中读取结果的空间
char Data[100] = {0};
while(1)
{
//清空空间
memset(Data, '\0', sizeof(Data));
int rd_count = read(fd, Data, sizeof(Data));
if(rd_count < 0)
{
perror("read error");
break;
}
else if(0 == rd_count)
{
printf("读取完毕!\n");
break;
}
else
{
printf("读取成功!\n");
printf("本次读取字符个数为:%d个\t读取结果为:Data=%s\n", \
rd_count, Data);
}
}
//关闭文件
close(fd);
return 0;
}
调用lseek()函数可以显示的定位一个已打开的文件
(1)头文件:
#include
#include
(2)函数原型:off_t Iseek(int fd, off_t offset, int whence);
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
//功能:创建一个空洞文件
//第一步:打开文件
int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0664);
if(fd < 0)
{
perror("open error");
return -1;
}
printf("open ok!\n");
//第二步:移动文件指示位置至自己指定的偏移量的位置
lseek(fd, 1024*1024*1024, SEEK_SET);
//第三步:在文件末尾写入一个字符:'\0'
write(fd, "\0", 1);
//第四步:关闭文件
close(fd);
return 0;
}
操作目录流程流程:
opendir readdir closedir
opendir
(1)头文件:
#include
#include
(2)函数原型:
DIR *opendir(const char *name);
DIR *:返回值是一个目录流指针,可以作为readdir的参数进行绑定需要读取目录的信息。
readdir
(1)头文件:
#include
(2)函数原型:
struct dirent *readdir(DIR *dirp);
struct dirent *:返回值代表需要读取的目录的信息
closedir
(1)头文件:
#include
#include
(2)函数原型:
int closedir(DIR *dirp);
DIR *:opendir函数的返回值
(1)头文件:
#include
#include
#include
(2)函数原型:
int stat(const char *pathname,struct stat *statbuf);
int fstat(int fd,struct stat *statbuf);
int lstat(const char *pathname,struct stat *statbuf);
(3)穿透函数:当测试的文件是软链接文件时,此时测得属性是属于软链接所指向的源文件的属性。
(4)非穿透函数:当测试的文件是软链接文件时,此时测得属性是软链接文件自身的属性。
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
//功能:实现打开目录,并读取目录中的文件的filename
//(1)opendir
DIR *pDir = opendir(argv[1]);
if(NULL == pDir)
{
perror("opendir error");
return -1;
}
printf("opendir ok!\n");
//定义struct stat结构体类型的变量
struct stat MyStat = {0};
//(2)readdir
while(1)
{
struct dirent *pSD = readdir(pDir);
if(NULL == pSD)
{
break;
}
if(*(pSD->d_name) == '.')
{
continue;
}
//使用pSD该结构体指针来访问结构体中的文件名
printf("文件名:%s\t",pSD->d_name);
//定义存储空间用来将文件名和其路径拼接之后的结果
char NewSpace[1024] = {0};
//拼接方式1
//strcat(NewSpace, argv[1]);
strcpy(NewSpace, argv[1]);
strcat(NewSpace, pSD->d_name);
//拼接方式2
//sprintf(NewSpace,"%s%s", argv[1], pSD->d_name);
if(lstat(NewSpace, &MyStat))
{
perror("lstat file error");
break;
}
//测试文件对应的大小
printf("文件大小:%ld\n",MyStat.st_size);
}
//关闭目录
closedir(pDir);
return 0;
}
(1)本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的。
(2)linux下的库:
静态库和共享库(动态库)二者的不同点在于代码被载入的时刻不同。
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
(1)库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。
(2)现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
(3)共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
(1)静态库对函数库的链接是放在编译时期 (compiletime) 完成的。
(2)编译时把静态库中的相关代码复制到可执行程序中。
(1)写源代码
eg:只要是一个文件,文件的内容是一个子函数即可 (目的是为了生成库)。
vim file_length.c / vim file_Line.c
(2)生成相应的o文件
gcc -c file_length.c -o file_length.o
gcc -c file_Linec -o file_Line.o
(3)创建成相应的静态库
静态库的命名方式:lib + 库名a
ar rcs libfile.a *,o
注意: libfile.a中的file是库名! !
(4)编译时链接静态库
gcc test.c -o test -L. -lfile
注意: -L.中的点代表当前路径(库的位置)
(5)运行
eg:./可执行文件名
(1)动态库把对一些库函数的链接载入推迟到程序运行的时期 (runtime)。
(2)编译时仅记录用到哪个共享库中的哪个符号,不复制共享库中的相关代码
(1)编写源文件vim file_length.c / vim file_Line.c
(2)生成相应的o文件
gec -c -fPIC file _length.c -o file_length.o
gcc -c -fPIC file_Line.c o file_Line.o
注意: -fPIC 要生成与位置无关的代码,可以在任何位置执行
(3)生成动态库(共享库)
gcc -shared *.o -o libfile.so.1
(4)为共享库文件创建软连接文件
In -s libfile.so.1 (绝对路径) libfle.so
(5)拷贝软链接文件到指定的目录/usr/lib下去
sudo cp libfile.so /usr/lib
(6)编译并链接共享库
gcc main_test.c -o myApp -lfile
此时的动态库的软链接必须存在于/usr/lib 目录下方可成功找到共享库
(7)运行myApp可执行文件即可
#gcc -fPIC -Wall -c hello.c
#gcc -shared -o libmyhello.so hello.o