笔记(十三):文件IO与标准IO

13 篇文章 1 订阅
9 篇文章 0 订阅

文件IO 进程 进程间通信,线程,线程同步,网络编程

一 文件IO

IO(input output)

1.1 学习IO的前提

1.为什么要学习IO
 嵌入式:由软件控制硬件,嵌入式需要对内存进行操作,和对内核进行操作,来完成操作硬件。
2.IO的内容都有什么:主要学习已经提供好的API函数接口
3.linux下一切皆文件
4.linux是一个多用户多任务的操作系统    ----》进程
5.因为IO进程在应用层和内核层之交互,所以说学习时定义过于抽象
6.linux操作系统把文件分为两大类
    (1)二进制文件  ---》a.out       --->按照计算机的逻辑去存储
    (2)文本文件    ---》.c或者.txt  --->按照程序员的逻辑去存储
   底层存储是一样,逻辑存储上不一样。

 1.2 IO是什么

IO指的是Input output,指的是对内存的输入和输出

 1.3 如何使用IO

需要借助概念:linux下一切皆文件

所以说对文件的操作:打开文件---------读写文件----------关闭文件

1.4 IO的分类

1.文件IO:是由操作系统提供的API接口,也被称为系统调用,每个操作系统的IO---API接口是不一样的,也就造成了linux下的可执行我呢见如果调用了文件IO的话,是不能移植到别的平台的,因为文件IO的接口是由操作系统提供,不同操作系统接口是不一样的。

2.标准IO:stdio.h ----->是由标准C库 ANSI C库提供的API接口,标准IO是在文件IO的基础上封装出来的。

1.5 文件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 中,用于标注错误信息,那么当函数报错返回时,会将这个全局变量重新根据错误信息表中的对用的号码设置一下

1.8 文件描述符

文件描述符是什么:一个文件的标志,是一个int类型的数据,非负数,值是小的。

 

 

注意:

./aout在刚运行时,内核会自动为他打开三个设备文件,返回三个文件描述:

1.  标准输入    -----0
2.  标准输出    -----1
3.  标准错误输出 ----2

1.9 API接口的使用

附加:函数的三要素:功能,参数,返回值

1.9 1 open

1.头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


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 + 想要修改的权限值

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char const *argv[])
{
    int fd = open("./1.txt",O_RDWR|O_CREAT,0666);   //打开一个文件
    if(-1 == fd)
    {
        perror("open");  //打印错误信息
        printf("errno = %d\n",errno);
        return -1;
    }
    perror("open1");
    return 0;
}

1.9.2 perror

例如:perror("helloworld")

perror是用来将上一个函数的错误原因输出到标准设备stderr。参数s所指的字符串先打印然后加上错误原因字符串。

头文件:
    #include <stdio.h>


原型:
    void perror(const char *s);
功能:以固定格式打印错误信息
参数:s:判断的函数名字
返回值:无

1.9.3 read

头文件:
#include <unistd.h>
       


原型:
    ssize_t read(int fd, void *buf, size_t count);
功能:对一个文件进行读取
参数:fd:目标文件描述符  ----》对哪个文件进行读取
      buf:存放读到的数据 ----》读到的数据存放的地址
      count:读多少
返回值:成功:返回读到的字节个数
        失败:返回-1
        读到文件末尾返回0
注意:但是最后依次read时如果文件剩余字节不够read的count,读到文件末尾时返回已经读到的字节个数,再一次读取时返回0
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

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 = %s\n",buf);
    //关闭文件描述符
    if(-1 == close(fd))
    {
        perror("close");
        return -1;
    }
    return 0;
}

1.9.4 write

头文件:
    #include <unistd.h>
原型:
    ssize_t write(int fd, const void *buf, size_t count);
功能:对一个文件进行写入操作
参数:fd:目标文件描述符 ----》对哪个文件进行操作
      buf:要写入的数据的地址----》要写入什么东西
      count:写入数据的大小  ---》写多少,以字节为单
返回值:
    成功:返回写入的字节个数
    失败: -1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
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;
}

1.9.5 close

头文件:    #include <unistd.h>        

原型:    int close(int fd);

功能:关闭一个文件参数:fd要关闭的文件描述符

返回值:    成功:返回 0    失败: -1注意:一个可执行程序最多打开1024个文件描述符,这个值可以通过内核去修改。

练习:先对一个文件进行写入helloworld,然后在从这个文件将helloworld读出来,打印到终端上。

结果:写入数据时正常,读取时未读到数据,这是因为读写指针的相互影响

1.9.6 lseek

头文件:       #include <sys/types.h>       #include <unistd.h>              

原型:     off_t lseek(int fd, off_t offset, int whence);

功能:操作读写指针,对读写指针进行偏移参数:    fd:目标文件描述符  ---》对哪个文件进行操作    offset:如何偏移,偏移多少        如果该数为负数,代表这向前进行偏移,如果偏移出了文件的开头,会报错返回        如果该数为正数,代表这向后偏移,如果偏移除了文件的末尾,会扩大文件,用'\0'来填充,那么此类文件被称为---空洞文件---   

注意:如果偏移后没有对其进行任何写入操作内核认为该偏移无效,不会扩大文件大小。    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

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <string.h>

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 = %s\n",buf);    close(fd);    return 0;}

练习:自己去实现cp命令 mycp(保证可以去拷贝二进制文件)

读取时循环读取,写入时循环写入,使用argv参数

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <string.h>#define SIZE 123int 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

2.1 标准IO的定义

标准IO是由标准C库提供的API接口,是在文件IO的基础上封装出来的函数接口,第一个原因是为了增加可移植性。并且,标准IO在文件IO的基础上封装了一个叫缓冲区的东西,用来存放一些不是很着急的数据,等到缓冲区满,调用一次文件IO完成写入工作或者读取工作。有些数据是特别着急的,

比如:下发的命令,实时监控数据,必须要使用文件IO。

伪代码:    fopen(参数)    {        if(is win)        {            _open(参数);                    }        else if(is linux)        {            open(参数);        }    }

文件流指针

FILE pointer

文件流指针:是一个文件的标志,是在文件描述符上封装出来的一个结构体指针,缓冲区就在这个流指针中。

流:流指针  -----文件流指针 ---这个流

追代码:

安装一个索引搜索文件tags(搜索引擎)

第一步:创建目录的引擎文件---tags (想要搜索哪个目录就在哪个目录下创建)    ctags -R   //生成一个引擎文件叫tags第二步:搜索    vi -t 目标字符串  例如: vi -t FILE第三步:选择准确的目录下的文件第四步:翻阅文档----》代码跳转    追代码:光标点击想要跳转的字符串 ,ctrl + ]进行跳转搜索    返回上一层:ctrl + t

 

 

2.2 标准IO函数接口

2.2.1 fopen

头文件: #include <stdio.h>原型:FILE *fopen(const char *pathname, const char *mode);功能:打开一个文件参数:pathname:要打开文件的路径及名称      mode:打开文件的方式      r:以只读的方式打开一个文件,文件必须存在      r+:以读写的方式打开一个文件,文件必须存在      w:如果文件存在则清空写入,如果文件不存在则创建写入      w+:如果文件存在则清空读写,如果文件不存在则创建读写      a:如果文件存在则追加写入,如果文件不存在则创建再写入      a+:如果文件不存在则创建文件并追加写入和读取,读取时从文件开头开始读取,写入时从文件的末尾开始写入,lseek偏移后不能进行写入操作          如果进行写入操作,则追加在末尾写入(不管之前如何偏移)注意:有可能以后会遇到 rb+,b代表执行二进制操作,linux下无区分返回值:    成功会返回一个文件流指针    失败返回NULL。

 

#include <stdio.h>int 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.2 fclose

头文件:   #include <stdio.h>              原型:    int fclose(FILE *stream);功能:关闭一个文件流指针  //文件流指针里也绑定了一个文件的描述符参数:目标文件流指针返回值:    成功:返回 0    失败: -1

2.2.3. fgetc

头文件:   #include <stdio.h>              原型:    int fgetc(FILE *stream);功能:从指定文件流指针中获取一个字符参数:目标文件流指针返回值:    成功:返回获取到的字符转化成int类型的数据    失败: 返回EOF    到达文件末尾: EOF

2.2.4 feof

头文件:   #include <stdio.h>              原型:    int feof(FILE *stream);功能:判断是否到达了文件末尾参数:目标文件流指针返回值:如果到达了文件的末尾返回一个非0值       其它情况返回0

2.2.5 ferror

头文件:   #include <stdio.h>              原型:    int ferror(FILE *stream);功能:判断文件操作是否出错参数:目标文件流指针返回值:如果文件操作失败了,返回一个非0值       其它情况返回0

#include <stdio.h>int 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---所对应的字符为%c\n",ret,ret);    }    if(fclose(fp) == EOF)    {        printf("关闭失败\n");        return -1;    }    return 0;}

2.2.6 fputc

头文件:   #include <stdio.h>              原型:        int fputc(int c, FILE *stream);功能:向指定的一个文件输出一个字符参数:c:想要写入的字符,将int转换成 unsigned char类型的数据写入    stream:目标文件流指针返回值:       成功返回:写入的字符转换成int类型的数据       失败返回:EOF

#include <stdio.h>#include <string.h>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;}

2.2.7 fgets

头文件:   #include <stdio.h>              原型:        char *fgets(char *s, int size, FILE *stream);功能:行读取参数:s:读取到的数据存放的地址      size:读取的字节个数      1.当\n之前的字节个数小于size,遇到\n结束读取      但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加'\0'.      2.当\n之前的字节个数大于size,会读到size个大小的字节时停止读取,      会在末尾加上一个'\0',会将最后一个字节的字符给覆盖掉。      机制会在读取完之后读写指针向前偏移一个字节      stream:目标文件流指针返回值:       成功返回:读到的字符串的首地址       失败返回:NULL       读到文件末尾:返回NULL

#include <stdio.h>int 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 = %s\n",buf);    }    fclose(fp);    return 0;}

2.2.8 fputs

头文件:   #include <stdio.h>              原型:        int fputs(const char *s, FILE *stream);功能:向一个文件进行字符串输入参数:s:想要写入的数据的地址     stream:目标文件流指针返回值:       成功返回:一个非负数       失败返回:EOF(-1)

#include <stdio.h>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;}

练习:请编写一个代码,用来测试文件的行数

#include <stdio.h>#include <string.h>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 = %s\n",buf);            count++;        }            }    count++;    printf("该文件的行数为:%d\n",count);    fclose(fp);    return 0;}

2.2.9 fread

头文件:   #include <stdio.h>              原型:        size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);功能:二进制读取文件参数:ptr:存放读到的数据的地址     size:对象的大小     nmemb:对象的个数     注意:总的读取的字节个数为对象的大小*对象的个数     stream:目标文件流指针返回值:       成功返回:成功返回读到的对象的个数       失败返回:0

2.2.10 fwrite

头文件:   #include <stdio.h> 原型:   size_t fwrite(const void *ptr, size_t size, size_t nmemb,                     FILE *stream);功能:二进制写入文件参数:ptr:想要写入的数据的首地址     size:对象的大小     nmemb:对象的个数     stream:目标文件流指针返回值:       成功返回:成功返回读到的对象的大小       失败返回:返回一个小的对象的个数

#include <stdio.h>#include <string.h>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("姓名:%s\n",p2.name);                printf("性别:%c\n",p2.sex);                printf("年龄:%d\n",p2.age);                printf("手机号:%s\n",p2.phone);            }        }    }        return 0;}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值