IO进程(标准IO)

本文围绕Linux下C语言标准IO展开,介绍了标准IO的概念、特点,阐述了全缓存、行缓存和不缓存三种缓存区类型。详细讲解了标准IO的函数接口,包括文件的打开、关闭、读写操作及其他操作,如重定向流和文件定位等,还给出了相关练习及思路。

大纲

IO:input、output

标准IO

文件IO

文件属性获取

目录操作

进程:process

进程基础

线程(thread)、同步、互斥、条件变量

进程间通信:6种(一共有7种)

无名管道(pipe)、有名管道(fifo)、信号(signal)、共享内存(shared memory)、信号灯集(semphore set)、消息队列(message queue)

标准IO

1.什么是标准IO?

1.1 概念

标准IO:是在C库中定义的一组专门用于输入输出的函数。

1.2 特点

1.通过缓冲机制减少系统调用,提高效率。

系统调用:是内核向上提供的一组接口

例如:从硬盘循环读1KB文件,每次读1B

2.围绕进行操作,流用FILE *来描述

注意: vi用ctags索引使用:

1)vi -t 查找名称

输入前面序号,回车。

2)继续追踪:

将光标定位到要追踪的内容,ctrl+]

回退:ctrl+t

3)跳转到上次位置:ctrl+o

      跳转到下次位置:ctrl+i

3.标准IO默认打开三个流,stdin(标准输入) stdout(标准输出) stderr(标准错误)

2.缓存区

  1. 全缓存:和文件相关的
  2. 行缓存:和终端相关的

刷新标准输出缓存区的条件:

  • \n
  • 程序正常退出
  • 缓存区满
  • 强制刷新:fflush(NULL)
#include <stdio.h>

int main(int argc, char const *argv[])
{
    //printf("hello world\n"); //\n不光可以换行,还可以刷新标准输出的缓冲区
    printf("hello world");
    fflush(NULL); //强制刷新缓存区
    while (1);
    return 0;  //正常退出刷新刷存区
}

     3.不缓存:没有缓存,标准错误。

综上:当我们每次要打印数据时,并不是将数据直接发送给标准输出设备,也就是并直接发送给显示器,而是将要打印的数据先存放到缓存区,当缓冲存数据满时,或者遇到\n,或者程序结束时,或者手动刷新缓存区时,缓冲区才会把数据传输到标准输出设备中,也就是显示器中进行输出。

例子:

#include <stdio.h>

int main()
{
    printf("hello");
    while(1);
    
    return 0;
}

结果是:不输出hello,什么也没有输出

因为:没有刷新缓冲区,只有刷新缓冲区的时候,缓冲区才会把数据传输到标准输出设备中

练习:计算标准输出的缓存区大小 

方法一:利用循环打印展示

方法二:利用结构体指针stdout

#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("buf:"); //不进行输出的话缓存去不会开辟,所以需要开辟缓存区的话需要先调用标准输出函数。
    printf("%d\n",stdout->_IO_buf_end - stdout->_IO_buf_base);
    return 0;
}

得到1024B=1KB

3.函数接口

3.1 打开文件 fopen

FILE *fopen(const char *path, const char *mode);

功能:打开文件

参数:

    path:打开的文件路径

    mode:打开的方式

        r:只读,当文件不存在时报错,文件流定位到文件开头

        r+:可读可写,当文件不存在时报错,文件流定位到文件开头

        w:只写,文件不存在创建,存在则清空

        w+:可读可写,文件不存在创建,存在则清空

        a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾

        a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾

注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置

返回值:

成功:文件流

失败:NULL,并且会设置错误码

3.2 关闭文件

int fclose(FILE* stream);

功能:关闭文件

参数:stream:文件流

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    //1.打开文件
    //fp = fopen("test.txt","w");
    fp = fopen("test.txt", "r");
    if (NULL == fp)
    {
        perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。
        return -1;
    }
    printf("fopen success\n");

    //2. 关闭文件
    fclose(fp);

    return 0;
}

3.3 文件读写操作

3.3.1 每次读写一个字符:fgetc()、fputc()

每次读一个字符fgetc()

int fgetc(FILE * stream);

功能:从文件中读取一个字符,并将当前文件指针位置向后移动一个字符。

参数:stream:文件流

返回值:成功:读到的字符

       失败或读到文件末尾:EOF(-1)

每次写一个字符fputc()

int fputc(int c, FILE * stream);

功能:向文件中写入一个字符, 成功写入后文件指针会自动向后移动一个字节位置。

参数:c:要写的字符

      stream:文件流

返回值:成功:写的字符的ASCII

       失败:EOF(-1)

(1)针对文件

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    //1.打开文件
    fp = fopen("test.txt", "r+");
    //fp = fopen("test.txt", "r");
    if (NULL == fp)
    {
        perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。
        return -1;
    }
    printf("fopen success\n");

    //对文件读写操作
    char ch = fgetc(fp); //test.txt的文件内容是hello
    printf("%c %d\n", ch, ch);//h 104

    ch = fgetc(fp);
    printf("%c %d\n", ch, ch);//e 101

    fputc('a', fp);
    fputc(98, fp);

    //2. 关闭文件
    fclose(fp);

    return 0;
}

(2)针对终端

#include <stdio.h>

int main(int argc, char const *argv[])
{
    char ch = fgetc(stdin);
    //printf("%d %c\n", ch, ch);
    fputc(ch, stdout);

    ch = fgetc(stdin);
    //printf("%d %c\n", ch, ch);
    fputc(ch, stdout);

    return 0;
}

补充:feof和ferror

int feof(FILE * stream);

功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符。

返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    //打开文件
    //fp = fopen("test.txt", "w"); //只写,文件不存在创建,存在则清空
    //fp = fopen("test.txt","r"); //只读,当文件不存在时报错,文件流定位到文件开头
    fp = fopen("test.txt","r+"); 
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    printf("fopen success\n");

    //对文件读写操作
    char ch = fgetc(fp);
    printf("%c %d\n",ch, ch); //h 104
    ch = fgetc(fp);
    printf("%c %d\n",ch, ch); //e 101

    fputc('w', fp);
    fputc('s', fp); //hewso

    //判断末尾
    ch = fgetc(fp);
    printf("%c %d\n",ch, ch); //o 111
    ch = fgetc(fp);
    printf("%c %d\n",ch, ch); //? -1
    if(feof(fp))
    {
        printf("end\n"); //-1之后才是末尾
    }

    //关闭文件
    fclose(fp);
    
    return 0;
}

int ferror(FILE * stream);

功能:检测文件有没有出错

返回:文件出错,返回非零值。如果没有出错,返回0

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    //1.打开文件
    fp = fopen("test.txt", "w");
    if (NULL == fp)
    {
        perror("fopen err"); 
        return -1;
    }
    printf("fopen success\n");

    char ch = fgetc(fp); //因为权限是只写,所以读操作有错误。
    if (ferror(fp))
    {
        printf("err!\n");
        return -1;
    }

    fclose(fp);

    return 0;
}

练习:cat 文件名

./a.out 文件名

步骤

  1. 打开文件
  2. 循环fgetc读文件内容
  3. 读到文件末尾EOF结束
  4. 读到内容fputc打印终端
  5. 关闭文件
#include <stdio.h>

//实现cat功能  将一个文件的内容打印到终端上
int main(int argc, char const *argv[])
{
    //判断
    if (argc != 2)
    {
        printf("format: %s <filename>\n", argv[0]);
        return -1;
    }
    //打开文件
    FILE *fp;
    fp = fopen(argv[1], "r");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    char ch;
    //循环读文件
    while (!feof(fp))
    {
        fputc(ch, stdout);
    }
    //关闭文件
    fclose(fp);

    return 0;
}

3.3.2 每次一串字符的读写 fgets() 和 fputs()

char * fgets(char *s, int size,  FILE * stream);

功能:从文件中每次读取一行字符串

参数:    s:存放字符串的地址

           size:期望一次读取的字符个数

         stream:文件流

返回值:成功:s的地址

       失败或读到文件末尾:NULL

特性:  每次实际读取的字符个数为size-1个,会在末尾自动添加\0

       每次读一行,遇到\n或者到达文件末尾后不再继续读下一行

       并把它存储在s所指向的字符串内。

int fputs(const char *s,  FILE * stream);

功能:向文件中写字符串

参数:s:要写的内容

     stream:文件流

返回值:成功:非负整数

       失败:EOF

(1)针对终端

注意: fgets输入最后一定有一个位置\0

(2)针对文件

练习:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)

思路文件循环读文件fgets(buf,32,fp)只要\n就len++

//实现 wc -l //将文件的行数打印到终端上
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char buf[55] = "";
    int len = 0;
    if (argc != 2) //如果命令数不是2,执行
    {
        printf("format: %s <filename>\n", argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1], "r");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }

    while (fgets(buf, 55, fp)) //读到末尾结束
    {
        if(buf[strlen(buf)-1] == '\n') //判断最后一个字符是不是“\n”
            ++len;
    }
    printf("%d %s\n", len, argv[1]);
    return 0;
}

练习:使用C语言编写一段程序,实现从1开始以每秒累加1的方式向文件中写入数字,写到100后停止。要求代码格式规范,输出结果清晰易懂。

//练习:使用C语言编写一段程序,实现从1开始以每秒累加1的方式向文件中
//写入数字,写到100后停止。要求代码格式规范,输出结果清晰易懂。
//sleep(1);

#include <stdio.h>
#include <unistd.h> //sleep函数的头文件
#include <string.h>

int main(int argc, char const *argv[])
{
    FILE *fp = fopen("test.txt", "a+");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    int len = 0;
    char buf[55] = "";
    //计算行数
    while (fgets(buf, 55, fp))
    {
        if(buf[strlen(buf)-1] == '\n')
            ++len;
    }
    //写入数字
    while (len != 100)
    {
        fprintf(fp, "%d\n", ++len);
        fflush(NULL);
        sleep(1); //睡眠1s
    }
    fclose(fp);

    return 0;
}

练习:

题目要求:编程读写一个文件test.txt,每隔1秒向文件中写入一行录入时间的数据,类似这样: 

1,  2007-7-30 15:16:42  

2,  2007-7-30 15:16:43

该程序应该无限循环,直到按Ctrl-C中断程序。

再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如: 

1,  2007-7-30 15:16:42

2,  2007-7-30 15:16:43

3,  2007-7-30 15:19:02

4,  2007-7-30 15:19:03

5,  2007-7-30 15:19:04

思路:

  1. 打开文件fopen,循环往文件写内容
  2. 每隔1s写入一行,sleep(1);
  3. 计算文件行数,wc -l
  4. 计算当前时间,转换成年月日、时分秒,time,localtime

man 2 time

time_t time(time_t *t);

如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。

  1. 字符串拼接函数:strcpy/strcat(dest, src)、sprintf、fprintf

fprintf:

格式化输出到流(stream)文件中,返回值是输出的字符数,发生错误时返回一个负值.

 int fprintf( FILE *stream, const char *format, ... );

sprintf:

格式化输出发送到buffer(缓冲区)中.返回值是写入的字符数量.

 int sprintf( char *buffer, const char *format, ... );

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int no = 1;
    char buf[55] = "";
    time_t tim;
    struct tm *t = localtime(&tim);
    
    //打开文件
    FILE *fp = fopen("hw.txt", "a+");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    //判断序号
    while(fgets(buf,55,fp))
    {
        if(buf[strlen(buf)-1] == '\n')
            ++no;
    }
    //写入时间
    while(1)
    {
        tim = time(NULL); //获取当前系统时间
        t = localtime(&tim); //转化为当地时间
        fprintf(fp, "%3d.%d-%d-%d %d:%d:%d\n", no++, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
        fflush(NULL);
        sleep(1);
    }
    //关闭文件
    fclose(fp);

    return 0;
}

3.3.3 二进制读写fread()和fwrite()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:从文件流读取多个元素(将二进制数据从文件读出)

参数:  ptr :是一个指针,是存放数据的存储空间的起始地址,用来存放读取元素

       size :元素大小  sizeof(元素数据类型)

       nmemb :读取元素的个数

       stream :要读取的文件流

返回值:成功:读取的元素的个数

       读到文件尾或失败: 0

size_t fwrite(const void *ptr, size_t size, size_t nmemb,

              FILE *stream);

功能:将二进制数据写入文件

参数: ptr :是一个指针,保存要输出数据的空间的地址。

     size :要写入的字节数 sizeof(数据类型)

     nmemb : 要进行写入元素的个数

      strem: 目标文件流指针

返回值:成功:写的元素个数

              失败 :-1

#include <stdio.h>

int main(int argc, char const *argv[])
{
    float arr[3] = {1.2, 3.4, 5.6};
    float data[3] = {};

    FILE *fp = fopen(argv[1], "r+");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    fwrite(arr, sizeof(float), 3, fp);

    //定位操作
    rewind(fp); //将位置指针定位到文件开头,不然是接着上一步写的位置继续读的,由于是末尾所以读不到东西。

    fread(data, 4, 3, fp);

    printf("%f %f %f\n", data[0], data[1], data[2]);

    return 0;

文件定位到开头:rewind(fp);

 

3.4 其他操作

3.4.1 重定向指定文件freopen

FILE * freopen(const char *pathname, const char *mode,  FILE* fp);

功能:将指定的文件流重定向到打开的文件中

参数:path:文件路径

     mode:打开文件的方式(同fopen)

     fp:文件流指针

返回值:成功:返回文件流指针

      失败:NULL

#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("hello\n");
    //将标准输出重定向到打开的test.txt中
    freopen("test.txt", "w+", stdout);
    printf("world\n");

    //将标准输出流重定向到终端文件/dev/tty
    freopen("/dev/tty", "r+", stdout);
    printf("66666\n");

    return 0;
}
3.4.2 文件定位操作

 

void rewind(FILE *stream);

功能:将文件位置指针定位到起始位置

int fseek(FILE *stream, long offset, int whence);

功能:文件的定位操作

参数:stream:文件流

     offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移

     whence:相对位置:

SEEK_SET:相对于文件开头

SEEK_CUR:相对于文件当前位置

SEEK_END:相对于文件末尾

返回值:成功:0

       失败:-1

注:当打开文件的方式为a或a+时,fseek不起作用    

补充:其中SEEK_SET,SEEK_CURSEEK_END和依次为012.

例子:

把fp指针移动到离文件开头100字节处:fseek(fp,100,0);

把fp指针移动到离文件当前位置100字节处:fseek(fp,100,1);

把fp指针退回到离文件结尾100字节处: fseek(fp,-100,2);

long ftell(FILE *stream);

功能:获取当前的文件位置

参数:要检测的文件流

返回值:成功:当前的文件位置,出错:-1

#include <stdio.h>

int main(int argc, char const *argv[])
{
    //fseek 文件定位操作
    //ftell 获取文件的当前指针位置
    FILE *fp = fopen("t1.txt", "w+");
    if (NULL == fp)
    {
        perror("fopen err");
        return -1;
    }
    //定位到文件开头往后10个位置
    fseek(fp, 10, 0);
    fputc('a', fp);
    //定位到相对于当前位置往后5个单位
    fseek(fp, 5, 1);
    fputs("hello", fp);
    //相对于最后位置往前1个单位
    fseek(fp, -1, 2);
    fputc('b', fp);

    //文件指针当前所在位置
    long l = ftell(fp);
    printf("%ld\n", l);



    return 0;
}

1.想对一个文本文件的尾部追加写入,应当在fopen何中使用的文件操作方式指示符号为 ()

A.r     B.w     C. a     D.w+

2.fseek(1, 2, 3);  这个函数是什么作用,三个参数分别是什么意思?

3.函数调用语句:fseek (fp,-10L,2);的含义是

A 将文件位置指针从文件未尾处向文件头的方向移动10个字节

B 将文件位置指针从当前位置向文件头的方向移动10个字节

C 将文件位置指针从当前位置向文件未尾方向移动10个字节

总结

为什么标准IO

1.因为读写文件通常是大量的数据(相对于底层驱动的系统调用所实现的数据操作单位),这时,使用库函数可以大大减少系统调用的次数

2.为了保证可移植性

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值