【IO进程线程】

最早接触的IO

#include <stdio.h> //标准IO的头文件
标准IO的接口:printf scanf

什么是标准IO/文件IO?

标准IO:库函数

fopen fgetc fputc fputs fgets fread fwrite fclose printf scanf...

文件IO:系统调用

open read write close...
什么是系统调用/什么是库函数?

系统调用:就是linux内核给用户提供的进入内核的接口,只要调用对应的函数就会从用户空间进入到内核空间。系统调用效率比较低(没有缓冲区),系统调用的移植性差。但不是说系统调用不重要,它有自己独特的使用场景,例如读取鼠标上报的键值就不需要缓冲区。

库函数:库函数=缓冲区+系统调用。库函数的效率比较高,库函数的移植性比较强。
在这里插入图片描述

标准IO

什么是FILE?

FILE是应用层的一个结构体,这个结构体是调用fopen的时候产生的,FILE是用来记录fopen打开时候的各种信息的结构体(只读,光标,缓冲区)。以后对这个文件的操作都是通过这个FILE完成的。在一个正在执行的程序中默认已经有三个文件指针了。

文件指针(FILE *)功能
stdin标准输入
stdout标准输出
stderr标准出错
/vscode快捷键
// 跳转 ctrl+鼠标左键
// 回退 alt + <-
// 对齐  alt + shift + f
// 注释  ctrl + /
// 开关终端 ctrl + j

typedef struct _IO_FILE FILE;
struct _IO_FILE {
    char* _IO_buf_base; /*缓冲区的起始地址 */
   char* _IO_buf_end; /*缓冲区的结束地址. */
};

fopen/fclose函数使用

man  man
1   可执行程序或 shell 命令
2   系统调用(内核提供的函数)
3   库调用(程序库中的函数)
    
 man 2 系统调用接口
 man 3 库函数接口
fopen/fclose的API
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:使用标准IO打开文件
参数:
    @pathname:打开的文件名  "/home/linux/1.c"
    @mode :打开文件的方式 "r" "r+"
       r      :以只读的方式打开文件,将光标定位到文件的开头
       r+     :以读写的方式打开文件,将光标定位到文件的开头
       w      :以只写的方式打开文件,如果文件存在就清空文件,如果文件不存在就创建文件
          将光标定位的开头
       w+     :以读写的方式打开文件,如果文件存在就清空文件,如果文件不存在就创建文件
          将光标定位的开头
       a      :以追加的方式打开文件,如果文件不存在就创建文件,如果文件存在将光标定位到文件结尾
       a+     :以读和追加的方式打开文件,如果文件不存在就创建文件,读光标在开头,写光标在结尾。
返回值:成功返回FILE的结构体指针,失败返回NULL,置位错误码

int fclose(FILE *stream);
功能:关闭文件
参数:
    @stream:文件指针
返回值:成功返回0,失败返回EOF(-1)(end of file),置位错误码
fopen/fclose的实例
#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    // 以只写的方式打开文件,如果文件不存在就创建文件
    // if ((fp = fopen("./hello.txt", "w")) == NULL) {
    //     printf("打开文件失败\n");
    //     return -1;
    // }

    // if ((fp = fopen("./hello.txt", "r")) == NULL) {
    //     printf("打开文件失败\n");
    //     return -1;
    // }

    if ((fp = fopen("./hello.txt", "a")) == NULL) {
        printf("打开文件失败\n");
        return -1;
    }

    // 关闭文件
    fclose(fp);
    printf("hello DC22091 everyone!!!!\n"); //就是在向标准输出中写数据
    // fclose(stdout); // 关闭标准输出
    printf("hello DC22091 everyone*****\n");//这句话不会在终端上显示

    fclose(stdin);    //关闭标准输入
    int a;
    scanf("%d",&a);  //scanf就会阻塞接收数据了
    printf("a = %d\n",a);

    return 0;
}

linux内核错误码问题

linux错误的问题

在这里插入图片描述

错误码使用实例
#include <stdio.h>
#include <errno.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
  
    if ((fp = fopen("./hello.txt", "r")) == NULL) {
        printf("打开文件失败,errno = %d\n",errno);
        return -1;
    }
    // 关闭文件
    fclose(fp);
   
    return 0;
}
将错误码转换为错误信息的函数
 #include <string.h>
char *strerror(int errnum);
功能:将错误码转换为错误信息的函数
参数:
    @errnum:错误码
返回值:错误信息
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
  
    if ((fp = fopen("./hello.txt", "r")) == NULL) {
        printf("打开文件失败,errno = %d\n",errno);
        //将错误码转换为错误信息,通过printf打印出来
        printf("%s\n",strerror(errno));
        return -1;
    }

    // 关闭文件
    fclose(fp);
   
    return 0;
}
perror打印错误函数
void perror(const char *s);
功能:只要错误码被置位了,它就可以直接打印出错误信息
参数:
    @s:用户自己附加的信息
返回值:无
#include <stdio.h>
int main(int argc, const char* argv[])
{
    FILE* fp;
  
    if ((fp = fopen("./hello.txt", "r")) == NULL) {
        //只要函数失败置位错误码,perror函数就可以将错误信息打印出来
        perror("open error\n");
        return -1;
    }

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

fgetc/fputc函数的使用

fgetc/fputc函数API
int fgetc(FILE *stream);
功能:从文件中读取一个字符(光标会随着读自动向后移动)
参数:
    @stream:文件指针
返回值:成功返回读取到字符的Ascii,失败返回EOF
        
int fputc(int c, FILE *stream);
功能:向文件中写一个字符(光标向后移动)
参数:
    @c:字符 
    @stream:文件指针
返回值:成功返回写字符的Ascii,失败返回EOF
fgetc函数的使用
#include <stdio.h>

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

    if((fp = fopen("./hello.txt","r"))==NULL){
        perror("fopen error");
        return -1;
    }

    printf("%c",fgetc(fp)); //从文件中读一个字符,打印到终端上,光标向后移动一个字符的位置
    printf("%c",fgetc(fp)); //文件每一行结尾都有换行符(除了最后一行,文件最后有EOF)
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%c",fgetc(fp));
    printf("%d",fgetc(fp));

    putchar(10);


    fclose(fp);

    return 0;
}
fputc函数的使用
#include <stdio.h>

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

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

    fputc('h', fp); //向文件中写入h字符,光标会向后移动一个字符的位置
    fputc('e', fp); //向文件中写入e字符,光标会向后移动一个字符的位置
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);

    fclose(fp);

    return 0;
}
fgetc/fputc的练习
1.编写一个程序用于统计文件行号

​ ./a.out hello.c

#include <stdio.h>

// ./a.out filename
int main(int argc, const char* argv[])
{
    FILE* fp;
    char ch;
    int count = 0;
    // 1.校验参数的个数
    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage : ./a.out filename\n");
        return -1;
    }
    // 2.以只读方式打开文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }
    // 3循环读取文件中的字符
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n') {
            count++;
        }
    }
    //4.打印行号
    printf("line = %d\n",count);

    //5.关闭文件
    fclose(fp);
    return 0;
}
使用fgetc和fputc实现文件的拷贝

./a.out srcfile destfile
注:可以使用diff命令比较文件 diff srcfile destfile 如果命令没有结果说明两个文件相同

#include <stdio.h>

// ./a.out srcfile destfile
int main(int argc, const char* argv[])
{
    FILE *fp1, *fp2;
    char ch;
    int count = 0;
    // 1.校验参数的个数
    if (argc != 3) {
        printf("input error,try again\n");
        printf("usage : ./a.out srcfile destfile\n");
        return -1;
    }
    // 2.以只读方式打开源文件
    if ((fp1 = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }
    // 3.以只写方式打开目标
    if ((fp2 = fopen(argv[2], "w")) == NULL) {
        perror("fopen error");
        return -1;
    }

    // 4.循环读取文件中的字符
    while ((ch = fgetc(fp1)) != EOF) {
        fputc(ch, fp2);
    }

    // 5.关闭文件
    fclose(fp1);
    fclose(fp2);
    return 0;
}

fgets/fputs函数的使用

fgets/fputs函数的API
char *fgets(char *s, int size, FILE *stream);
功能:从文件(stdin)中读取一个字符串到s中
    fgets遇到EOF或者'\n'的时候就会停止,会将'\n'存储到s中,s中存放字符的结尾是'\0'
参数:
    @s:内存的首地址
    @size:想要读取的字符的个数(最多size-1)
    @stream:文件指针(fp  stdin)
返回值:成功返回的是s,如果出错或到了文件结尾返回NULL

int fputs(const char *s, FILE *stream);
功能:将s中的内容写入到文件中(stdout)
参数:
    @s:字符串的首地址
 @stream:文件指针
返回值:成功返回>0的数,失败返回EOF(-1)
fgets使用实例
1.使用fgets从文件中读取字符串
#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    char s[128] = {0};
    if ((fp = fopen("./hello.txt", "r")) == NULL) {
        perror("fopen error");
        return -1;
    }
    //从文件中读取字符串到s中,遇到‘\n’或EOF会停止
    fgets(s,sizeof(s),fp);
    puts(s);
    fgets(s,sizeof(s),fp);
    puts(s);

    fclose(fp);
    return 0;
}
fgets从标准输入中读取字符串
#include <stdio.h>
#include <string.h>
int main(int argc, const char* argv[])
{
    FILE* fp;
    char s[128] = {0};

    // hello\n\0
    fgets(s,sizeof(s),stdin);
    //将终端上输入的'\n'清除
    s[strlen(s)-1]='\0';
    printf("s = %s\n",s);

    return 0;
}
使用fgets统计文件的行号

./a.out filename
char s[10];

#include <stdio.h>
#include <string.h>
// ./a.out filename
int main(int argc, const char* argv[])
{
    FILE* fp;
    char s[10] = { 0 };
    int line = 0;
    // 1.校验参数的个数
    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage : ./a.out filename\n");
        return -1;
    }
    // 2.以只读方式打开文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }
    // 3循环读取文件中的字符
    while (fgets(s, sizeof(s), fp) != NULL) {
        if (strlen(s) == (sizeof(s) - 1)) {
            if (s[sizeof(s) - 2] != '\n')
                continue;
        }
        line++;
    }
    // 4.打印行号
    printf("line = %d\n", line);

    // 5.关闭文件
    fclose(fp);
    return 0;
}
fputs使用实例
#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    char s[] = "sdfasdfasfd****fasdfasdfas\n";
    if ((fp = fopen("./hello.txt", "w")) == NULL) {
        perror("fopen error");
        return -1;
    }
    fputs(s,fp); //向文件中写入字符串

    fputs(s,stdout);//向终端上写入字符串

    fclose(fp);
    return 0;
}
练习:使用fgets和fputs实现文件的拷贝

​ ./a.out srcfile destfile

#include <stdio.h>

// ./a.out srcfile destfile
int main(int argc, const char* argv[])
{
    FILE *fp1, *fp2;
    char s[128] = {0};
    int count = 0;
    // 1.校验参数的个数
    if (argc != 3) {
        printf("input error,try again\n");
        printf("usage : ./a.out srcfile destfile\n");
        return -1;
    }
    // 2.以只读方式打开源文件
    if ((fp1 = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }
    // 3.以只写方式打开目标
    if ((fp2 = fopen(argv[2], "w")) == NULL) {
        perror("fopen error");
        return -1;
    }

    // 4.循环读取文件中的字符
    while (fgets(s,sizeof(s),fp1)) {
        fputs(s,fp2);
    }

    // 5.关闭文件
    fclose(fp1);
    fclose(fp2);
    return 0;
}

将系统的时间写入到文件中

获取系统时间的API
#include <time.h>

time_t time(time_t *tloc);
功能:获取从1970-1-1 00:00:00到当前的秒钟数
参数:
    @tloc:写为NULL
返回值:成功返回秒钟数,失败返回-1置位错误码
        
struct tm *localtime(const time_t *timep);
功能:将秒钟转换为tm的结构体,这个结构体内部就是年月日时分秒
参数:
    @timep:秒钟数的地址
返回值:成功返回tm结构体指针,失败返回NULL,置位错误码
           struct tm {
               int tm_sec;    //秒
               int tm_min;    //分钟
               int tm_hour;   //小时
               int tm_mday;   //天
               int tm_mon;    //月 +1
               int tm_year;   //年 + 1900
               int tm_wday;   //周几(0-6, Sunday = 0) 
               int tm_yday;   //在一年内的天数
               int tm_isdst;  //夏令时
           };
获取系统时间实例
#include <stdio.h>
#include <time.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    time_t ts;
    struct tm* tm;

    if ((ts = time(NULL)) == -1)
        PRINT_ERR("get time error");

    if ((tm = localtime(&ts)) == NULL)
        PRINT_ERR("change time error");

    printf("%d-%02d-%02d %02d:%02d:%02d\n",
    tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);    
    return 0;
}
snprintf函数
int snprintf(char *str, size_t size, const char *format, ...);
功能:将字符串格式化到str中
参数:
    @str:内存的首地址
 @size:字符个数(最多将size-1个字符格式化到str中,最后存放的是'\0')
    @format,...:和printf的用法一样
 返回值:成功返回大于0的数(格式化字符的个数),失败返回负数       
#include <stdio.h>

int main(int argc,const char * argv[])
{
    char s[128] = {0};
    char *p = "hello world";
    char ch='W';
    int year = 2022;
    int month=11;
    
    //功能将双引号内部的字符串写入到s的数组中,不向终端显示
    snprintf(s,sizeof(s),"%04d-%02d-%s-%c",year,month,p,ch);
    
    //将s中的数据显示到终端上
    printf("%s\n",s);
    return 0;
}
练习:将当前的时间写入到time.txt的文件中,如果ctrl+c退出之后,在再次执行支持断点续写

1.2022-11-04 19:10:20
2.2022-11-04 19:10:21
3.2022-11-04 19:10:22
//按下ctrl+c停止,再次执行程序
4.2022-11-04 20:00:00
5.2022-11-04 20:00:01

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

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int get_file_line(FILE *fp)
{
    char ch=0;
    int line=0;
    while((ch = fgetc(fp))!=EOF){
        if(ch=='\n'){
            line++;
        }
    }
    return line;
}
int main(int argc, const char* argv[])
{
    FILE *fp;
    time_t ts, ots;
    struct tm* tm;
    int line=0;
    char buf[128] = {0};

    if((fp = fopen("./time.txt","a+"))==NULL)
        PRINT_ERR("fopen error");

    line = get_file_line(fp);

    ts = ots = 0;
    while (1) {
        if ((ts = time(NULL)) == -1)
            PRINT_ERR("get time error");
        if (ts != ots) {
            ots = ts;
            if ((tm = localtime(&ts)) == NULL)
                PRINT_ERR("change time error");

            snprintf(buf,sizeof(buf),"%4d.%d-%02d-%02d %02d:%02d:%02d\n",
                line++,tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
          
            fputs(buf,fp);

            fflush(fp); //强制刷新缓冲区
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值