零、概述一、文件IO
1.1 学习IO的前提1.2 IO是什么1.3 如何使用IO1.4 IO的分类1.5 文件IO的接口1.6 man手册的使用1.7 errno1.8 文件描述符1.9 API接口的使用
1.9 1 open1.9.2 perror1.9.3 read1.9.4 write1.9.5 close1.9.6 lseek1.10 练习:自己去实现cp命令 mycp(保证可以去拷贝二进制文件) 二、标准IO
2.1 标准IO的定义
2.1.1 文件流指针 2.2 标准IO函数接口
2.2.1 fopen2.2.2 fclose2.2.3. fgetc2.2.4 feof2.2.5 ferror2.2.7 fgets2.2.8 fputs练习:请编写一个代码,用来测试文件的行数2.2.9 fread2.2.10 fwrite
零、概述文件IO 进程 进程间通信,线程,线程同步,网络编程
一、文件IOIO(input output)
1.1 学习IO的前提- 为什么要学习IO
嵌入式:由软件控制硬件,嵌入式需要对内存进行操作,和对内核进行操作,来完成操作硬件。IO的内容都有什么:主要学习已经提供好的API函数接口linux下一切皆文件linux是一个多用户多任务的操作系统 ----》进程因为IO进程在应用层和内核层之交互,所以说学习时定义过于抽象linux操作系统把文件分为两大类
(1)二进制文件 —》a.out —>按照计算机的逻辑去存储(2)文本文件 —》.c或者.txt —>按照程序员的逻辑去存储 底层存储是一样,逻辑存储上不一样。
1.3 如何使用IOIO指的是Input output,指的是对内存的输入和输出
需要借助概念:linux下一切皆文件
所以说对文件的操作:打开文件---------读写文件----------关闭文件
- 文件IO:是由操作系统提供的API接口,也被称为系统调用,每个操作系统的IO—API接口是不一样的,也就造成了linux下的可执行我呢见如果调用了文件IO的话,是不能移植到别的平台的,因为文件IO的接口是由操作系统提供,不同操作系统接口是不一样的。标准IO:stdio.h ----->是由标准C库 ANSI C库提供的API接口,标准IO是在文件IO的基础上封装出来的。
1. open
2. read
3. write
4. close
5. lseek
注意:windows函数接口:_open _read _write _close
ios函数接口: Open Read Write Close
1.6 man手册的使用
man:是一个在线函数查询接口
1 Executable programs or shell commands( 可执行程序或者shell命令) 2 **System calls (functions provided by the kernel)(系统调用)(内核提供的函数) 3 **Library calls (functions within program libraries)(库调用)(程序库中的函数) 4 Special files (usually found in /dev) (特殊文件)(通常位于 /dev) 5 File formats and conventions eg /etc/passwd (文件格式和规范) 6 Games (游戏) 7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) 杂项(包括宏包和规范) 8 System administration commands (usually only for root) (系统管理命令)(通常只用于管理用户) 9 Kernel routines [Non standard](内核例程)【非标准】1.7 errno
errno是一个全局变量,存在于#include
文件描述符是什么:一个文件的标志,是一个int类型的数据,非负数,值是小的。
注意:
./aout在刚运行时,内核会自动为他打开三个设备文件,返回三个文件描述:
- 标准输入 -----0标准输出 -----1标准错误输出 ----2
附加:函数的三要素:功能,参数,返回值1.9 1 open
1.头文件: #include#include #include 2.原型: int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 3.功能:打开一个文件 4.参数: pathname:路径及名称 例如:"1.txt" //以后参数是char*类型一般直接写字符串 flags:打开文件的方式 必须包括: O_RDONLY:只读权限打开文件 O_WRONLY:只写权限打开文件 O_RDWR:读写方式打开文件 附加参数(以或的形式去附加) O_APPEND:以追加写的方式打开文件 O_CREAT:如果文件存在则打开文件,如果文件不存在则创建文件,如果使用了O_CREAT这个参数,需要使用open的第三个参数,给与权限是一个8进制的整形,以0开头 例如:0664 O_EXCL:如果文件存在则报错返回 O_NONBLOCK:以非阻塞方式打开一个文件 O_TRUNC:以清空的方式打开文件 5.返回值: 成功返回一个文件描述符 失败返回-1; 注意:使用0666创建文件为什么出来的是0664呢? 是由操作系统有一个叫做文件掩码的机制,将其它用户权限的可写权限抹掉 使用umask查看文件掩码 修改文件掩码 umask + 想要修改的权限值
#include1.9.2 perror#include #include #include #include int main(int argc, char const *argv[]) { int fd = open("./1.txt",O_RDWR|O_CREAT,0666); //打开一个文件 if(-1 == fd) { perror("open"); //打印错误信息 printf("errno = %dn",errno); return -1; } perror("open1"); return 0; }
头文件:
#include
原型:
void perror(const char *s);
功能:以固定格式打印错误信息
参数:s:判断的函数名字
返回值:无
1.9.3 read
头文件: #include原型: ssize_t read(int fd, void *buf, size_t count); 功能:对一个文件进行读取 参数:fd:目标文件描述符 ----》对哪个文件进行读取 buf:存放读到的数据 ----》读到的数据存放的地址 count:读多少 返回值:成功:返回读到的字节个数 失败:返回-1 读到文件末尾返回0 注意:但是最后依次read时如果文件剩余字节不够read的count,读到文件末尾时返回已经读到的字节个数,再一次读取时返回0
#include1.9.4 write#include #include #include #include #include int main(int argc, char const *argv[]) { //打开文件----》以读的方式打开 int fd = open("./1.txt",O_RDONLY); if(-1 == fd) { perror("open"); return -1; } //开始读取数据 char buf[3] = {0}; if(-1 == read(fd,buf,3)) //有多少空间读取多少个字节 { perror("read"); return -1; } printf("n读取到的值为buf = %sn",buf); //关闭文件描述符 if(-1 == close(fd)) { perror("close"); return -1; } return 0; }
头文件:
#include
原型:
ssize_t write(int fd, const void *buf, size_t count);
功能:对一个文件进行写入操作
参数:fd:目标文件描述符 ----》对哪个文件进行操作
buf:要写入的数据的地址----》要写入什么东西
count:写入数据的大小 ---》写多少,以字节为单
返回值:
成功:返回写入的字节个数
失败: -1
#include1.9.5 close#include #include #include #include #include #include int main(int argc, char const *argv[]) { int fd = open("./1.txt",O_WRONLY); if(-1 == fd) { perror("open"); return -1; } //开始写入数据 char buf[123] = "hello world"; if(-1 == write(fd,buf,strlen(buf))) { perror("write"); return -1; } return 0; }
头文件:
#include
原型:
int close(int fd);
功能:关闭一个文件
参数:fd要关闭的文件描述符
返回值:
成功:返回 0
失败: -1
注意:一个可执行程序最多打开1024个文件描述符,这个值可以通过内核去修改。
练习:先对一个文件进行写入helloworld,然后在从这个文件将helloworld读出来,打印到终端上。
结果:写入数据时正常,读取时未读到数据,这是因为读写指针的相互影响
头文件:
#include
#include
原型:
off_t lseek(int fd, off_t offset, int whence);
功能:操作读写指针,对读写指针进行偏移
参数:
fd:目标文件描述符 ---》对哪个文件进行操作
offset:如何偏移,偏移多少
如果该数为负数,代表这向前进行偏移,如果偏移出了文件的开头,会报错返回
如果该数为正数,代表这向后偏移,如果偏移除了文件的末尾,会扩大文件,用' '来填充,那么此类文件被称为---空洞文件---
注意:如果偏移后没有对其进行任何写入操作内核认为该偏移无效,不会扩大文件大小。
whence:基准位置 ----》根据哪一个位置进行偏移
SEEK_SET :根据文件开头进行偏移
The file offset is set to offset bytes.
SEEK_CUR:根据用户当前位置进行偏移
The file offset is set to its current location plus offset bytes.
SEEK_END:根据文件末尾进行偏移
The file offset is set to the size of the file plus offset bytes.
返回值:
成功:返回偏移的字节个数(根据文件开头来定)
失败: -1
#include1.10 练习:自己去实现cp命令 mycp(保证可以去拷贝二进制文件)#include #include #include #include #include #include int main(int argc, char const *argv[]) { int fd = open(argv[1],O_RDWR|O_CREAT,0666); if(-1 == fd) { perror("open"); return -1; } //开始进行写入数据 if(-1 == write(fd,"hello world",11)) { perror("write"); return -1; } if(-1 == lseek(fd,-11,SEEK_END)) { perror("lseek"); return -1; } char buf[123] = {0}; if(-1 == read(fd,buf,sizeof(buf))) { perror("read"); return -1; } printf("buf = %sn",buf); close(fd); return 0; }
//读取时循环读取,写入时循环写入,使用argv参数 #include二、标准IO 2.1 标准IO的定义#include #include #include #include #include #include #define SIZE 123 int main(int argc, char const *argv[]) { if(3 != argc) { printf("输入参数错误,例如:./a.out + 源文件 + 目标文件n"); return -1; } //打开要复制的文件以只读方式打开,打开要粘贴的文件,以写入并创建并且清空 int fd = open(argv[1],O_RDONLY); if(-1 == fd) { perror("open"); return -1; } int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664); if(-1 == fd1) { perror("open1"); return -1; } //开始读取 ---》边读边写 while(1) { //先读 char buf[SIZE] = {0};//作为数据的中转站 ssize_t ret = read(fd,buf,sizeof(buf)); if(-1 == ret) { perror("read"); return -1; } if(0 == ret) { printf("读取到文件的末尾,退出while循环n"); break; } //写入 if(-1 == write(fd1,buf,ret)) { perror("write"); return -1; } } close(fd); close(fd1); return 0; }
标准IO是由标准C库提供的API接口,是在文件IO的基础上封装出来的函数接口,第一个原因是为了增加可移植性。并且,标准IO在文件IO的基础上封装了一个叫缓冲区的东西,用来存放一些不是很着急的数据,等到缓冲区满,调用一次文件IO完成写入工作或者读取工作。有些数据是特别着急的,
比如:下发的命令,实时监控数据,必须要使用文件IO。
伪代码:
fopen(参数)
{
if(is win)
{
_open(参数);
}
else if(is linux)
{
open(参数);
}
}
2.1.1 文件流指针
FILE pointer
文件流指针:是一个文件的标志,是在文件描述符上封装出来的一个结构体指针,缓冲区就在这个流指针中。
流:流指针 -----文件流指针 —这个流
追代码:
安装一个索引搜索文件tags(搜索引擎)
2.2 标准IO函数接口 2.2.1 fopen第一步:创建目录的引擎文件—tags (想要搜索哪个目录就在哪个目录下创建)
ctags -R //生成一个引擎文件叫tags
第二步:搜索
vi -t 目标字符串 例如: vi -t FILE
第三步:选择准确的目录下的文件
第四步:翻阅文档----》代码跳转
追代码:光标点击想要跳转的字符串 ,ctrl + ]进行跳转搜索
返回上一层:ctrl + t
头文件: #include
原型:FILE *fopen(const char *pathname, const char *mode);
功能:打开一个文件
参数:pathname:要打开文件的路径及名称
mode:打开文件的方式
r:以只读的方式打开一个文件,文件必须存在
r+:以读写的方式打开一个文件,文件必须存在
w:如果文件存在则清空写入,如果文件不存在则创建写入
w+:如果文件存在则清空读写,如果文件不存在则创建读写
a:如果文件存在则追加写入,如果文件不存在则创建再写入
a+:如果文件不存在则创建文件并追加写入和读取,读取时从文件开头开始读取,写入时从文件的末尾开始写入,lseek偏移后不能进行写入操作
如果进行写入操作,则追加在末尾写入(不管之前如何偏移)
注意:有可能以后会遇到 rb+,b代表执行二进制操作,linux下无区分
返回值:
成功会返回一个文件流指针
失败返回NULL。
#include2.2.2 fcloseint main(int argc, char const *argv[]) { //打开一个文件需要定义一个文件流指针来指向fopen返回的已经封装好的文件流指针 //FILE *fp = fopen("./1.txt","r"); FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("open"); return -1; } printf("打开文件成功!n"); return 0; }
2.2.3. fgetc头文件:
#include
原型:
int fclose(FILE *stream);
功能:关闭一个文件流指针 //文件流指针里也绑定了一个文件的描述符
参数:目标文件流指针
返回值:
成功:返回 0
失败: -1
2.2.4 feof头文件:
#include
原型:
int fgetc(FILE *stream);
功能:从指定文件流指针中获取一个字符
参数:目标文件流指针
返回值:
成功:返回获取到的字符转化成int类型的数据
失败: 返回EOF
到达文件末尾: EOF
2.2.5 ferror头文件:
#include
原型:
int feof(FILE *stream);
功能:判断是否到达了文件末尾
参数:目标文件流指针
返回值:如果到达了文件的末尾返回一个非0值
其它情况返回0
头文件:
#include
原型:
int ferror(FILE *stream);
功能:判断文件操作是否出错
参数:目标文件流指针
返回值:如果文件操作失败了,返回一个非0值
其它情况返回0
#includeint main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //开始做循环读取 while(1) { int ret = fgetc(fp); if(EOF == ret) { if(feof(fp)) { printf("到达了文件末尾!n"); break; } else { printf("获取失败!n"); return -1; } } printf("获取到的数据为%d---所对应的字符为%cn",ret,ret); } if(fclose(fp) == EOF) { printf("关闭失败n"); return -1; } return 0; }
2.2.6 fputc
头文件:
#include
原型:
int fputc(int c, FILE *stream);
功能:向指定的一个文件输出一个字符
参数:c:想要写入的字符,将int转换成 unsigned char类型的数据写入
stream:目标文件流指针
返回值:
成功返回:写入的字符转换成int类型的数据
失败返回:EOF
#include2.2.7 fgets#include int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("fopen"); return -1; } //开始向文件中写入数据 char buf[123] = "hello world"; int i = 0; while(1) { fputc(buf[i],fp); if((strlen(buf) - 1) == i) { break; } i++; //遍历数组使用 } fclose(fp); return 0; }
头文件:
#include
原型:
char *fgets(char *s, int size, FILE *stream);
功能:行读取
参数:s:读取到的数据存放的地址
size:读取的字节个数
1.当n之前的字节个数小于size,遇到n结束读取
但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加’ ’.
2.当n之前的字节个数大于size,会读到size个大小的字节时停止读取,
会在末尾加上一个’ ’,会将最后一个字节的字符给覆盖掉。
机制会在读取完之后读写指针向前偏移一个字节
stream:目标文件流指针
返回值:
成功返回:读到的字符串的首地址
失败返回:NULL
读到文件末尾:返回NULL
#include2.2.8 fputsint main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //行读取 while(1) { char buf[123] = {0}; if(fgets(buf,11,fp) == NULL) { //失败或者读到文件末尾 if(feof(fp)) { printf("读到了文件的末尾n"); break; } else { perror("fgets"); return -1; } } printf("buf = %sn",buf); } fclose(fp); return 0; }
头文件:
#include
原型:
int fputs(const char *s, FILE *stream);
功能:向一个文件进行字符串输入
参数:s:想要写入的数据的地址
stream:目标文件流指针
返回值:
成功返回:一个非负数
失败返回:EOF(-1)
#include练习:请编写一个代码,用来测试文件的行数int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("fopen"); return -1; } //想要写入的数据的地址 char buf[123] = "hello world"; if(EOF == fputs(buf,fp)) { perror("fputs"); return -1; } fclose(fp); return 0; }
#include2.2.9 fread#include int main(int argc, char const *argv[]) { FILE *fp = fopen(argv[1],"r"); if(NULL == fp) { perror("fopen"); return -1; } int count = 0; //开始计算 while(1) { char buf[123] = {0}; if(NULL == fgets(buf,sizeof(buf),fp)) { //出错或者到了文件末尾 if(feof(fp)) { printf("到达了文件的末尾n"); break; } else { perror("fgets"); return -1; } } if(buf[strlen(buf) - 1] == 'n') { printf("buf = %sn",buf); count++; } } count++; printf("该文件的行数为:%dn",count); fclose(fp); return 0; }
2.2.10 fwrite头文件:
#include
原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE stream);
功能:二进制读取文件
参数:ptr:存放读到的数据的地址
size:对象的大小
nmemb:对象的个数
注意:总的读取的字节个数为对象的大小对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的个数
失败返回:0
头文件:
#include
原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:二进制写入文件
参数:ptr:想要写入的数据的首地址
size:对象的大小
nmemb:对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的大小
失败返回:返回一个小的对象的个数
#include#include struct person { char name[32]; char sex; int age; char phone[12]; }; struct person p1; //定义一个录入使用的结构体 struct person p2; //定义一个读取时使用的结构体 int main(int argc, char const *argv[]) { STAT: //界面: printf("****************************n"); printf("1.录入信息 2 读取信息 3.退出n"); printf("****************************n"); printf("input>> "); int input = 0; scanf("%d",&input); if (input > 3 || input < 0) { goto STAT; } if(1 == input) { printf("请输入姓名n"); scanf("%s",p1.name); getchar(); printf("请输入性别n"); scanf("%c",&p1.sex); getchar(); printf("请输入年龄n"); scanf("%d",&p1.age); printf("请输入手机号n"); scanf("%s",p1.phone); getchar(); FILE *fp = fopen("./1.txt","a"); if(NULL == fp) { perror("fopen"); return -1; } if(1 != fwrite(&p1,sizeof(p1),1,fp)) { perror("fwrite"); return -1; } fclose(fp); printf("写入信息结束,正在跳转菜单"); goto STAT; } else if(2 == input) { char usrname[32] = {0}; //读取信息 printf("请输入要查询人的姓名n"); scanf("%s",usrname); getchar(); //开始对文件进行读取并匹配 FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //循环读取 while(1) { if(0 == fread(&p2,sizeof(p2),1,fp)) { if(feof(fp)) { printf("读到文件末尾n"); break; } else { perror("fread"); return -1; } } if(strcmp(usrname,p2.name) == 0) { printf("姓名:%sn",p2.name); printf("性别:%cn",p2.sex); printf("年龄:%dn",p2.age); printf("手机号:%sn",p2.phone); } } } return 0; }



