IO进程-IO

input/output 输入输出,针对于文件的输入输出。

linux中有7种文件类型,b(块设备block)c(字符设备)d(目录文件)-(普通文件)l (链接文件)s (套接字文件)p(管道)

标准IO

概念

定义:在c库中的一组专门用于输入输出的函数

特点:通过缓冲机制减少系统调用次数,提高效率。

围绕“”进行操作,流用FILE *描述

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

只能操作普通文件

缓存区:

全缓存:和文件相关

行缓存:和终端相关

不缓存:没有缓存区,标准错误

刷新缓存条件

全缓存

  1. 程序的正常退出

  1. 缓存区满刷新

  1. fflush强制刷新

行缓存

  1. 程序的正常退出

  1. \n

  1. 缓存区满刷新

  1. fflush强制刷新

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

#include<stdio.h>
int main (int argc, const char *argv[])
{
    printf("zise:");//开辟缓冲区
    printf("%d\n",stdout->_IO_buf_end-stdout->_IO_buf_base);//标准输出的缓冲区的结束地址-起始地址
    return 0;
}

函数接口

打开文件-读写文件-关闭文件-定位操作

1、打开文件
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
功能:打开文件
参数:path:打开文件的路径
     mode:打开方式
        r    只读,流被定义在文件开头
        r+    可读可写,流被定义在文件开头
        w    只写,文件不存在创建文件,存在则清空文件,流被定义在文件开头
        w+    可读可写,文件不存在创建,存在清空,流被定位到文件开头
        a    追加,文件不存在创建,存在追加,流被定义在文件末尾
        a+    可读可写,文件不存在创建,存在追加;第一次读流被定义在文件开头,写始终在文件末尾
返回值:
    成功:打开文件的文件流
    失败:NULL,并且设置errno
重定向打开文件
FILE * freopen(const char *pathname,  const char *mode,  FILE* fp)
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
      fp:文件流指针
返回值:成功:返回文件流指针
      失败:NULL

练习:将标准输出重定向到当前路径下的out.c文件中,在将标准输出重定向到终端文件中。

#include<stdio.h>
int main(int argc, const char *argv[])
{
    printf("hello\n");//输出在终端
    freopen("out.c","w+",stdout);//重定向到out.c文件中
    printf("world\n");//输出在out.c文件中
    freopen("/dev/tty","r+",stdout);//重定向到终端文件中
    printf("123\n");//输出在终端
    return 0;
}
2、读写文件
每次一个字符的读写
int fgetc(FILE *stream)
功能:从文件中读一个字符
参数:stream:文件流
返回值:
    成功:
    读到字符的ASCII码
    失败或读到文件末尾:EOF//EOF=-1,以下都是

int fputc(int c, FILE * stream)
功能:向文件中写入一个字符
参数:c:要写的字符
     stream:文件流
返回值:
    成功:写的字符的ASCII
    失败:EOF

练习:编程实现cat功能。cat 文件名//a.out 文件名

#include<stdio.h>
int main(int argc, const char *argv[])
{
    FILE *fd=fopen(argv[1],"r");//引用函数打开文件
    if(NULL==fd)//容错判断
    {
        perror("fopen err");//打印错误信息
        return -1;
    }
    int ascii;//定义int类型接受一下ASCII码
    while((ascii=fgetc(fd))!=-1)//循环打印
        fputc(ascii,stdout);
    return 0;
}

补充:

int  feof(FILE * stream);
功能:判断文件有没有到结尾
返回:到达文件末尾,返回非零值

int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值
每次一串字符的读写
char * fgets(char *s,  int size,  FILE * stream);
功能:从文件中每次读取一行字符串
参数:s:存放字符串的地址
         size:一次读取的字符个数
         stream:文件流
 返回值:成功:s的地址
       失败或读到文件末尾:NULL
特性:每次实际读取的最大字符个数为size-1个,会在末尾自动添加\0
     遇到\n结束一次读

int  fputs(const char *s,  FILE * stream);
功能:向文件中写字符串
参数:s:要写的内容
        stream:文件流
返回值:成功:非负整数
       失败:EOF

练习:编程实现wc -l命令功能。计算文件行数

#include<stdio.h>
#include<string.h>
int main(int argc, const char *argv[])
{
    FILE *fd=fopen(argv[1],"r");//引用函数打开文件
    if(NULL==fd)//容错判断
    {
        perror("fopen err");//打印错误信息
        return -1;
    }
    char buf[32];//用于存放字符串
    int num=0;//记录行数
    while(fgets(buf,32,fd)!=NULL)//读文件一直到结尾
        if(buf[strlen(buf)-1]=='\n')//根据特点(遇到\n结束一次读)判断条件
            num++;
    printf("%d %s\n",num,argv[1]);
    return 0;
}
二进制数据读写
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素
参数:    ptr :用来存放读取元素
        size :元素大小  sizeof(数据类型)
        nmemb :读取对象的个数
        stream :要读取的文件流
返回值:成功:读取对象的个数
      读到文件尾: 0
       失败: -1

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:按对象写
参数:同上    
返回值:成功:写的元素个数
      失败 :-1

fread和fwrite函数注意:
1)两个函数的返回值为:读或写的对象数
2)对于二进制数据我们更愿意一次读或写整个结构。
3、关闭文件
int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流

练习:编程实现“head -n 文件名”的功能

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char const *argv[])
{
    if(argc!=3)
    {
        printf("Usage:%s <-n> <file>\n",argv[0]);
        return -1;
    }
    FILE *f=fopen(argv[2],"r");
    if(NULL==f)
    {
        perror("fopen err");
        return -1;
    }
    int n= atoi(&argv[1][1]);//要打印几行
    printf("%d\n",n);
    int i=0;
    char sh[32]={};
    while(n!=i&&fgets(sh,32,f)!=NULL)
    {
        if(sh[strlen(sh)-1]=='\n')
                i++;
        printf("%s",sh);
        fflush(NULL);
    }
    fclose(f);
    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

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
int main(int argc, const char *argv[])
{
    FILE *fd=fopen("test.txt","a+");//引用函数打开文件
    if(NULL==fd)//容错判断
    {
        perror("fopen err");//打印错误信息
        return -1;
    }
    int num=0;
    char buf[32];
    while(fgets(buf,32,fd)!=NULL)//判断文件要从第几行开始
        if(buf[strlen(buf)-1]=='\n')
            num++;
    time_t t;
    struct tm *ti;
    while(1)//循环录入
    {
        t=time(NULL);
        ti=localtime(&t);
        fprintf(fd,"%d, %d-%d-%d %d:%d:%d\n",++num,ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec);
        fflush(NULL);//刷新缓存区,发数据刷新到文件中
        sleep(1);
    }
    return 0;
}
4、文件定位操作
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不起作用 
             
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1

fseek(fp, 0, SEEK_SET); ==>  rewind(fp);

文件IO

概念

定义:在posix(可移植操作系统接口)中定义的一组用于输入输出的函数

特点:没有缓冲机制,每次操作都会引起系统调用

围绕“文件描述符”进行操作,文件描述符是非负整数,顺序分配

默认打开三个文件描述符:0(标准输入)、1(标准输出)、2(标准错误)

可以操作任意类型文件,除目录文件

函数接口

打开文件-读写文件-关闭文件-定位操作

1、打开文件
int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
      flags:打开文件的方式
            O_RDONLY:只读
            O_WRONLY:只写
            O_RDWR:可读可写
            O_CREAT:创建
            O_TRUNC:清空
            O_APPEND:追加   
返回值:成功:文件描述符
        失败:-1

注意:
当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限 
int open(const char *pathname, int flags, mode_t mode);
创建出来的文件权限为指定权限值&(~umask)  //umask为文件权限掩码0002
与标准IO打开方式对应关系

标准IO

文件IO

r

O_RDONLY

r+

O_RDWR

w

O_WRONLY|O_CREAT|O_TRUNC,0666(需要指定文件权限)

w+

O_RDWR|O_CREAT|O_TRUNC,0666(同上)

a

O_WRONLY|O_CREAT|O_APPEND,0666(同上)

a+

O_RDWR|O_CREAT|O_APPEND,0666(同上)

2、读写文件
ssize_t read(int fd, void *buf, size_t count);
功能:从一个已打开的可读文件中读取数据
参数:fd  文件描述符
          buf  存放位置
         count  期望的个数
返回值:成功:实际读到的个数
      返回-1:表示出错,并设置errno号
      返回0:表示读到文件结尾

ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd   文件描述符
          buf   要写的内容
          count  期望值
返回值:成功:实际写入数据的个数
              失败  : -1
3、关闭文件
int close(int fd);
功能:关闭文件
参数:fd:文件描述符

练习:编程实现cp命令功能。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
int main(int argc, char const *argv[])
{
    int fd=open(argv[1],O_RDONLY);
    if(fd<0)
    {
        perror("open fd err");
        return -1;
    }
    int fb=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(fb<0)
    {
        perror("open fb err");
        return -1;
    }
    char buf[32];
    ssize_t s;
    while((s=read(fd,buf,32))!=0)
        write(fb,buf,s);
    close(fd);
    close(fb);
    return 0;
}
4、定位操作
off_t lseek(int fd, off_t offset, int whence);
功能:设定文件的偏移位置
参数:fd:文件描述符
    offset偏移量  
        正数:向文件结尾位置移动
        负数:向文件开始位置
    whence  相对位置
        SEEK_SET   开始位置
        SEEK_CUR   当前位置
        SEEK_END   结尾位置
返回值:成功:文件的当前位置
        失败:-1

标准IO和文件IO对比

标准IO

文件IO

定义

c库中定义的一组输入输出函数

posix中定义的一组输入输出函数

特点

有缓冲机制

围绕流进行操作

默认打开三个流:stdin/stdout/stderr

只能操作普通文件

无缓冲机制

围绕文件描述符操作

默认打开三个文件描述符:0/1/2

除目录外可以操作任意类文件

函数

打开文件:fopen/freopen

读写文件:fgetc/fputc、fgets/fputs、fread/fwrite

关闭文件:fclose

文件定位:fseek、ftell、rewind

打开文件:open

读写文件:read/write

关闭文件:close

文件定位:lseek

文件属性获取

int stat(const char *path, struct stat *buf);
功能:获取文件属性
参数:path:文件路径名
       buf:保存文件属性信息的结构体
返回值:成功:0
      失败:-1
struct stat {
        ino_t     st_ino;     /* inode号 */
        mode_t    st_mode;    /* 文件类型和权限 */
        nlink_t   st_nlink;   /* 硬链接数 */
        uid_t     st_uid;     /* 用户ID */
        gid_t     st_gid;     /* 组ID */
        off_t     st_size;    /* 大小 */
        time_t    st_atime;   /* 最后访问时间 */
        time_t    st_mtime;   /* 最后修改时间 */
        time_t    st_ctime;  /* 最后状态改变时间 */
    };

练习: 编程实现“ls -l 文件名”命令的功能

hq@Ubuntu:~/22121/IO/day2$ ls -l 1.c
-rw-rw-r-- 1 hq hq 527 2月   8 11:47 1.c

文件类型和权限 硬链接数 用户名 组名 大小 最后修改时间 文件名

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
int main(int argc, char const *argv[])
{
    struct stat s;
    if (stat(argv[1], &s) != 0)//容错判断
    {
        perror("stat err");
        return -1;
    }
    switch (s.st_mode & S_IFMT)//判断文件类型
    {
        case S_IFSOCK:putchar('s');break;
        case S_IFLNK:putchar('l');break;
        case S_IFREG:putchar('-');break;
        case S_IFBLK:putchar('b');break;
        case S_IFDIR:putchar('d');break;
        case S_IFCHR:putchar('c');break;
        case S_IFIFO:putchar('p');break;
    }
    
    char mod[4]="rwx-";//判断文件权限
    for(int i=0;i<9;i++)
        printf("%c",(s.st_mode&0400>>i)?mod[i%3]:mod[3]);

    printf(" %d ", s.st_nlink);//硬链接数
    struct passwd *p = getpwuid(s.st_uid);//用户名
    printf("%s ", p->pw_name);
    struct group *q = getgrgid(s.st_gid);//组名
    printf("%s ", q->gr_name);
    printf("%ld ", s.st_size);//大小

    time_t t = s.st_mtime;//最后修改时间
    struct tm *ti = localtime(&t);
    printf("%d月   %d %d:%d ", ti->tm_mon + 1, ti->tm_mday, ti->tm_hour, ti->tm_min);
    printf("%s\n", argv[1]);//文件名
    return 0;
}

目录操作

打开目录-读目录-关闭目录

1、打开目录

DIR *opendir(const char *name);
功能:获得目录流
参数:要打开的目录
返回值:成功:目录流
       失败:NULL

2、读目录

struct dirent *readdir(DIR *dirp);
功能:读目录
参数:要读的目录流
返回值:成功:读到的信息    
      失败或读到目录结尾:NULL
注意:
返回值为结构体,该结构体成员为描述该目录下的文件信息
struct dirent {
        ino_t   d_ino;                 /* 索引节点号*/
        off_t   d_off;               /*在目录文件中的偏移*/
        unsigned short d_reclen;    /* 文件名长度*/
        unsigned char  d_type;      /* 文件类型 */
        char    d_name[256];      /* 文件名 */
};

3、关闭目录

int closedir(DIR *dirp);
功能:关闭目录
参数:dirp:目录流

练习:实现ls命令功能。不显示隐藏文件

hq@Ubuntu:~/22121/IO/day2$ ls -a
.   11.c  a.out   liu.c    open.c  stat.c       text.c
..  1.c   head.c  lseek.c  out.c   .stat.c.swp
hq@Ubuntu:~/22121/IO/day2$ ls
11.c  1.c  a.out  head.c  liu.c  lseek.c  open.c  out.c  stat.c  text.c

ls打开的是当前目录,而且里面没有以“.”开头的隐藏文件

#include<stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char const *argv[])
{
    DIR *dir=opendir(".");//打开当前目录
    if(NULL==dir)//容错判断
    {
        perror("opendir err");
        return -1;
    }
    struct dirent *d;
    while((d=readdir(dir))!=NULL)//读当前目录
        if(d->d_name[0]!='.')//排除以“.”开头的文件
            printf("%s\t",d->d_name);
    putchar(10);
    closedir(dir);//关闭目录
    return 0;
}

补充:

·源文件:xx.c 包含main函数的.c、包含子函数的.c

头文件:xx.h 函数声明、头文件、结构体/共用体/枚举定义、typedef、define

库文件:如下

库的定义

当使用别人的函数时除了包含头文件以外还要有库

库:就是把一些常用函数的目标文件打包在一起,提供相应函数的接口,便于程序员使用;本质上来说库是一种可执行代码的二进制形式

由于windows和linux的本质不同,因此二者库的二进制是不兼容的

库的分类

静态库和动态库,本质区别是代码被载入时刻不同。

1) 静态库在程序编译时会被连接到目标代码中。

优点:程序运行时将不再需要该静态库;运行时无需加载库,运行速度更快

缺点:静态库中的代码复制到了程序中,因此体积较大;

静态库升级后,程序需要重新编译链接

2) 动态库是在程序运行时才被载入代码中。

优点:程序在执行时加载动态库,代码体积小;

程序升级更简单;

不同应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

缺点:运行时还需要动态库的存在,移植性较差

静态库的制作

1-将源文件编译生成目标文件

gcc -c add.c -o add.o

2-创建静态库用ar命令,它将很多.o转换成.a

ar crs libmyadd.a add.o

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a

3-测试使用静态库:

gcc main.c -L. -lmyadd // -L指定库的路径 -l指定库名

执行./a.out

动态库的制作

1-我们用gcc来创建共享库

gcc -fPIC -c hello.c -o hello.o

-fPIC 创建与地址无关的编译程序

gcc -shared -o libmyhello.so hello.o

2-测试动态库使用

gcc main.c -lmyhello

可以正常编译通过,但是运行时报错./a.out: error while loading shared libraries: libmyadd.so: cannot open shared object file: No such file or directory

原因:当加载动态库时,系统会默认从/lib或/usr/lib路径下查找库文件

解决方法(有三种):

(1)把库拷贝到/usr/lib和/lib目录下。(此方法编译时不需要指定库的路径)

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

(终端关闭,环境变量就没在了)

(3) 添加/etc/ld.so.conf.d/*.conf文件。把库所在的路径加到文件末尾,并执行ldconfig刷新

sudo vi xx.conf

添加动态库存在的路径,如:

/home/hq/teach/22092/day3/dynamic

补充:

库默认的查找路径:/lib 或 /usr/lib

头文件默认的查找路径:/usr/include

#include <stdio.h> :从系统路径下查找

#include “head.h” :从当前路径下查找

gcc编译选项:

-L路径:指定库的路径

-I路径:(i大写)指定头文件的路径

-l库名:(L小写)指定库文件

当头文件不在当前路径下时,两种解决方法:

1. gcc main.c -I头文件路径

2. 将头文件存放在系统路径下/usr/include

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值