I/O 课程DAY1

I/O 课程DAY1_第1张图片

 

一、课程特点

1> IO的课程需要学习很多接口(函数),大概有100个左右,需要大家多记、多练,每天要完成思维导图绘制

2> 我们可以通过本门课程与linux内核打交道

3> 充分利用课堂时间

二、课程大纲

1> IO知识

标准IO、文件IO

2> 进程

进程的创建、进程间通信

3> 线程

线程的创建、线程同步互斥

4> Linux系统库

静态库、动态库

三、IO知识

2.1最先接触的io知识:#include

1> std:标准的

2> IO:输入输出,目前接触的是向终端进行输入输出

(向文件中写入数据就是输出)

2.2 IO的种类

1.标准IO:库函数实现

2.文件IO:系统调用实现,来自于#include

2.3什么是库函数?什么是系统调用

1.系统调用:从用户空间到内核空间的一次切换过程

   不同的系统,进入内核空间的方式是不同的,所以,接口函数有所不同

   但是,只要从用户空间切换到内核空间,就会发生系统调用,效率比较低

2.库函数:

    库函数是对系统调用的封装:缓冲区+系统调用

     库函数有缓冲区,系统调用没有缓冲区

I/O 课程DAY1_第2张图片

3.4 常用的函数接口的种类

        1. 标准IO:printf、scanf、puts、gets、getchar、putchar;

                fopen、fclose、fprintf、fscanf、fputs、fgets、fgetc、fputc、

                fread、fwrit

        2.文件IO:open、close、read、write

四、标准IO

4.1什么是FILE结构体

        是系统定义的一个结构体类型,用于记录文件的相关操作

4.2 如何找到FILE结构体

        1.在/usr/include路径下:sudo ctags -R 生成索引文件(tags)

        2.在当前目录下:vi -t FILE 追该名字的出处

 struct _IO_FILE {


  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */

4.3 特殊的文件指针

        1.对于文件的操作,都需要使用文件指针来完成

        2.自定义文件指针:FILE *fp;

        3.系统提供的特殊文件指针:stdin(标准输入)、stdout(标准输出指针)、stderr(标准出错)

4.4 fopen的使用  

man手册的使用

man后面跟上数字的含义:可以使用man man

2 系统调用(由Linux内核提供的相关函数)

3 库调用(程序库中的函数)

有时可以不用加数字,man手册会自动从第一个进行往下排查

#include                  //所需头文件

FILE *fopen(const char *pathname, const char *mode);
功能:使用标准IO打开一个文件
参数1:文件路径,字符串
参数2:也是一个字符串,打开模式
       r      Open text file for reading.  The stream is positioned at the beginning of the file.
               打开一个文件,进行只读功能,指针定位在文件开头

       r+     Open for reading and writing.  The stream is positioned at the beginning of the file.
               打开一个文件,既可读又可写,指针定位在文件开头

       w      Truncate file to zero length or create text file for writing.  The stream is positioned at the begin‐
              ning of the file.
              只写形式打开一个文件,如果文件不存在,创建一个,如果存在,则删掉重新创建,指针定位在文件开头

       w+     Open for reading and writing.  The file is created if it does not exist, otherwise it  is  truncated.
              The stream is positioned at the beginning of the file.
              读写形式打开一个文件,如果文件不存在,创建一个,如果存在,则删掉重新创建,指针定位在文件开头
              

       a      Open  for  appending (writing at end of file).  The file is created if it does not exist.  The stream
              is positioned at the end of the file.
              以追加形式打开一个文件,在末尾写,指针定位在末尾

       a+     Open for reading and appending (writing at end of file).  The file is created if it does  not  exist.
              The  initial file position for reading is at the beginning of the file, but output is always appended
              to the end of the file.
              以追加的形式打开文件,读从开头读,写从末尾写,如果文件不存在,则创建文件
返回值:成功打开返回打开文件的地址,失败返回NULL,并置位错误码
#include
#include
#include

int main(int argc, const char *argv[])
{
    FILE *fp;        //定义文件指针,用于指向要打开的文件

    /*
    //只写形式打开文件,如果文件不存在,则创建一个空文件
    fp = fopen("./01file.txt", "w"); 
    */

    //以只读的形式打开文件,如果文件不存在,则打开失败
    //fp = fopen("./01file.txt", "r"); 
    

    //以追加的形式打开文件,如果文件不存在,则创建一个空文件
    fp = fopen("./01file.txt", "a"); 

    //判断文件指针是否为NULL
    if(fp == NULL)
    {
        printf("fopen error\n");
        return -1;
    }else
    {
        printf("open file okk\n");
    }

    return 0;
}

4.5 fclose的使用

  #include 

       int fclose(FILE *stream);
功能:关闭给定文件指针指向的文件
参数:要关闭的文件指针
返回值:成功返回0,失败返回EOF,并置位错误码

#include
#include
#include

int main(int argc, const char *argv[])
{
    FILE *fp;        //定义文件指针,用于指向要打开的文件

    /*
    //只写形式打开文件,如果文件不存在,则创建一个空文件
    fp = fopen("./01file.txt", "w"); 
    */

    //以只读的形式打开文件,如果文件不存在,则打开失败
    //fp = fopen("./01file.txt", "r"); 
    

    //以追加的形式打开文件,如果文件不存在,则创建一个空文件
    fp = fopen("./01file.txt", "a"); 

    //判断文件指针是否为NULL
    if(fp == NULL)
    {
        printf("fopen error\n");
        return -1;
    }else
    {
        printf("open file okk\n");
    }

    fclose(fp);      //关闭fp指针指向的文件
    fclose(stdin);    //关闭输入输入指针
    fclose(stdout);       //关闭标准输出指针
    fclose(stderr);        //关闭标准错误指针

    return 0;
}

4.6 关于错误码的问题

在文件IO或标准IO相关接口被调用是,如果出错了,操作系统会给应用程序返回一个错误码,一共又4096,每一个代表不同的错误

I/O 课程DAY1_第3张图片

 I/O 课程DAY1_第4张图片

 1> 处理错误码的两个函数

1>
      #include 
       void perror(const char *s); 
       功能:打印错误码对应的信息
       参数:打印错误码对应信息前的附加信息,不要加格式控制符,只是单纯的字符串
       返回值:无
2>    
       #include 
       char *strerror(int errnum);
       功能:根据错误码。转换错误信息
       参数:错误码,如何获取,需要包含头文件:#include
                       extern int errno;
                       用的时候只需要填errno即可
       返回值:错误信息的字符串   

2> strerror的使用

I/O 课程DAY1_第5张图片

 3> perror的使用

I/O 课程DAY1_第6张图片

 4.7 fgetc/fputc函数的使用

      #include 

       int fputc(int c, FILE *stream);
功能:向给定文件中输出一个字符
参数1:被输出字符的ascii值
参数2:要输出的文件指针
返回值:成功返回ascii值,失败返回EOF
 
 #include 

 int fgetc(FILE *stream);
 功能:从文件中读取一个字符到程序中
 参数:文件指针
 返回值:成功返回读取字符的ascii值,读取失败或者文件结束返回EOF

1> fputc的实例

#include
#include
#include

int main(int argc, const char *argv[])
{
    FILE *fp;       //定义文件指针

    //打开文件
    if((fp = fopen("./01file.txt", "w")) == NULL)
    {
        perror("open file");
        return -1;
    }

    //文件已经打开成功
    //将数据写入文件
    fputc('h', fp);
    fputc('e', fp);
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);
    

    //关闭文件
    fclose(fp);

    return 0;
}

2> fgetc的实例

#include
#include
#include

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp;

    //以只读的形式打开文件
    if((fp = fopen("./01file.txt", "r")) == NULL)
    {
        perror("open file");
        return -1;
    }


    //定义一个存储字符的变量
    char ch;

    //处理文件中的多个字符,重复性的动作使用循环解决
    while( (ch = fgetc(fp)) != EOF )
    {
        printf("%c", ch);
    }


    //关闭文件
    fclose(fp);

    return 0;
}

I/O 课程DAY1_第7张图片

 练习1:使用fgetc统计一个文件的行号

#include
#include
#include

int main(int argc, const char *argv[])
{
    //对参数个数进行判断
    if(argc != 2)
    {
        printf("file count error\n");
        printf("usage: ./a.out fileName\n");
        return -1;
    }

    //定义文件指针,打开文件
    FILE *fp;

    if((fp = fopen(argv[1], "r")) == NULL)
    {
        perror("open file");
        return -1;
    }

    //读取字符
    char ch;
    int line = 0;

    while((ch = fgetc(fp)) != EOF)
    {
        if(ch == '\n')
        {
            line++;
        }
    }

    //关闭文件
    fclose(fp);

    //输出行号
    printf("一共%d行\n", line);


    return 0;
}

练习2:使用fgetc和fputc完成两个文件的拷贝

#include
#include
#include

int main(int argc, const char *argv[])
{
    //判断传进来的文件个数是否为3
    if(argc !=3 )
    {
        printf("file count error\n");
        printf("usage: ./a.out srcfile destfile\n");
        return -1;
    }

    //定义两个文件指针,分别指向源文件和目标文件
    FILE *srcfp, *destfp;
    //以只读的形式打开源文件
    if((srcfp=fopen(argv[1], "r")) == NULL)
    {
        perror("srcfile open");
        return -1;
    }

    //以只写的形式打开目标文件
    if((destfp = fopen(argv[2], "w")) == NULL)
    {
        perror("destfile open");
        return -1;
    }

    //定义字符搬运工
    char ch;
    
    while((ch = fgetc(srcfp)) != EOF)
    {
        //将字符写入到目标文件中去
        fputc(ch, destfp);
    }

    //关闭两个文件
    fclose(srcfp);
    fclose(destfp);

    
    return 0;
}

4.8 fgets/fputs函数的使用

  #include 



       int fputs(const char *s, FILE *stream);              
功能:将指定的字符串s,写入到给定的文件中
参数1:要写入的文件字符串
参数2:文件指针
返回值:成功返回成功写入字符的个数(非负整数),失败返回EOF

       char *fgets(char *s, int size, FILE *stream);
功能:从给定的文件stream中,读取至少小于size个字符,放入到给定字符数组s中
        遇到换行或者EOF停止读取,换行符号也会被读取到s中,在全部读取结束后,会自定加一个'\0'
参数1:要存放数据的字符数组
参数2:读取的大小(读取的字符串实际长度为size-1)
参数3:文件指针
返回值:成功返回s数组,失败返回

1> fgets的实例

#include
#include
#include

int main(int argc, const char *argv[])
{
    char buf[1024];         //定义存储数据的字符数组

    printf("请输入一个字符串:");

    fgets(buf, sizeof(buf), stdin);
    //从标准输入中获取一个字符串,放到buf中
    
    buf[strlen(buf) -1] = '\0';
    
    printf("buf = %s", buf);

    return 0;
}

2> fputs的实例

#include
#include
#include

int main(int argc, const char *argv[])
{
    //向标准输出文件中存放数据
    fputs("hello world", stdout);


    //向标准出错文件中写数据
    fputs("shang hai", stderr);

    //向自定义文件中写数据
    FILE *fp;
    if((fp = fopen("./07file.txt", "w")) == NULL)
    {
        perror("open file");
        return -1;
    }

    //向文件中写入数据
    fputs("I love China\n", fp);

    //关闭文件
    fclose(fp);

    return 0;
}

五、Linux系统中的ctags的使用

5.1ctags是什么

linux提供的追代码的工具

5.2怎么使用ctags

  1>.创建索引文件

2> 追代码:vi -t 函数名\变量名

3> 使用:ctrl + ],进行继续深层追代码

4> 返回上一级:ctrl + t

六、关于缓冲区问题

6.1 缓冲区的大小

1> 全缓存:跟自定义文件指针有关的缓冲区,其大小为4096字节

2> 行缓存:跟终端相关的操作使用的是行缓存(stdin、stdout),其大小为1024字节

3> 不缓存:跟标准出错有关的操作是不缓存(stderr),其大小为0

#include
#include
#include

int main(int argc, const char *argv[])
{
    //航缓存的大小为1024字节
    //如果没有使用行缓存,则默认大小为0
    printf("行缓存:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);

    //只有完成一次输入时,才能求出行缓存区的大小
    int num;
    scanf("%d", &num);
    printf("stdin行缓存:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);

    printf("stdout行缓存:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);



    fputs("hello world\n", stderr);
    fputs("hello world\n", stderr);
    //不缓存的大小为0
    printf("不缓存:%ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base);


    //全缓存,大小为4096
    FILE *fp;
    if((fp = fopen("./08file.txt", "w")) == NULL)
    {
        perror("open file");
        return -1;
    }

    fputc('H', fp);


    printf("全缓存:%ld\n", fp->_IO_buf_end - fp->_IO_buf_base);
    fclose(fp);


    return 0;
}

6.2 缓冲区的刷新时间

1> 行缓存的刷新时机

1、遇到‘\n’会刷新行行缓存

2、程序结束后,自动刷新行缓存

3、当输入输出发生切换时,会刷新行缓存

4、当关闭文件指针时,也会刷新行缓存

5、手动刷新缓存区时,行缓存会进行刷新

6、当缓存区满了后,会自动刷新缓存区

#include
#include
#include

int main(int argc, const char *argv[])
{
    /*
    printf("hello world");          //没有'\n'不会刷新缓存区
    //1、当遇到'\n'时会刷新缓冲器
    printf("hello world\n");
    while(1);
    */

    //2、当程序结束后,会自动刷新行缓存
    //printf("hello world");
    

    /*
    //3、当输入输出发生切换时,会刷新行缓存
    printf("hello world");
    int num;
    scanf("%d", &num);
    while(1);
    */

    /*
    //4、当关闭文件指针时,也会刷新行缓存
    printf("hello world");
    fclose(stdout);           //关闭文件指针
    while(1);
    */

    /*
    //5、手动刷新缓存区时,行缓存会进行刷新
    printf("hello world");
    fflush(stdout);            //手动刷新缓存区
    while(1);
    */

    //6、当缓存区满了后,会自动刷新缓存区
    for(int i=0; i<1025; i++)
    {
        printf("%c", 'a');
    }
    while(1);


    return 0;
}

2> 全缓存的刷新时机

1、遇到‘\n’不会刷新全缓存

2、程序结束后,自动刷新全缓存

3、当输入输出发生切换时,会刷新全缓存

4、当关闭文件指针时,也会刷新全缓存

5、手动刷新缓存区时,会刷新全缓存

6、当缓存区满了后,会自动刷新缓存区

#include
#include
#include

int main(int argc, const char *argv[])
{
    FILE *fp;

    if((fp = fopen("./10file.txt", "w")) == NULL)
    {
        perror("open file");
        return -1;
    }

    /*
    //1、全缓存遇到'\n'不会刷新缓冲区
    fputc('A', fp);
    fputc('\n', fp);
    while(1);
    */

    //2、程序运行结束后,会刷新全缓存
    //fputc('A', fp);
    
    /*
    //3、当你输入输出发生切换时, 会刷新全缓存
    fputc('A', fp);
    fgetc(fp);
    while(1);
    */

    /*
    //4、当关闭文件时,会刷新全缓存
    fputc('A', fp);
    fclose(fp);
    while(1);
    */

    /*
    //5、手动刷新缓存区时,也会刷新缓存区
    fputc('A', fp);
    fflush(fp);
    while(1);
    */

    //6、缓存区满了,会刷新全缓存
    for(int i=0; i<4097; i++)
    {
        fputc('a', fp);
    }

    while(1);
    

    return 0;
}

作业:

1> 使用fgets统计一个文件的行号

2> 使用fgets、fputs拷贝文件

1.

I/O 课程DAY1_第8张图片

 I/O 课程DAY1_第9张图片

2.

I/O 课程DAY1_第10张图片

 I/O 课程DAY1_第11张图片

 

你可能感兴趣的:(linux)