Linux应用(一)文件与IO


Linux下一切皆文件,那么掌握对于文件的操作:IO就是十分重要的,掌握IO实际上就是掌握IO的一堆编程接口,还是很好用的;(毕竟后面一直在用)

一 简略的概念

1 文件相关

1.1 Linux下的文件类型

常规文件 r
目录文件 d
字符设备文件 c
块设备文件 b
管道文件 p
套接字文件 s
符号链接文件 l

2 系统调用

系统调用就是操作系统提供的接口函数。
如果我们把系统调用封装成库函数就可以起到隔离的作用,提高程序的可移植性。
Printf就是库函数然后调用了系统调用才在显示器上显示字符。

二 标准IO

1 标准IO相关

标准I/O由ANSI C标准定义,标准I/O通过缓冲机制减少系统调用,实现更高的效率

1.1 标准IO的流

  • FILE结构体
    标准IO(stdio.h)用一个结构体类型来存放打开的文件的相关信息
    标准I/O的所有操作都是围绕结构体FILE来进行
  • 流(stream)
    FILE又被称为流(stream)
    文本流/二进制流

有三个预定义的流,在每个进程中自动创建:
stdin;stdout;stderr;它们都是FILE流结构体;

1.2 缓冲类型

  • 全缓冲
    对于输出,缓冲区满才输出;
  • 行缓冲
    对于输出,遇到换行符输出;
  • 无缓冲
    数据直接写入文件,流不进行缓冲;

标准I/O预定义3个流,程序运行时自动打开
stdin/stdout 默认是行缓冲
stderr没有缓冲

2 标准IO的编程接口

2.1 文件的打开关闭

打开文件:

#include <stdio.h>
FILE *fopen (const char *path, const char *mode);

返回值:出错返回NULL,并设置errno;

  • Path:普通文件当前路径下不需要加目录,其他要加上完整的路径与文件名
  • Mode:
    在这里插入图片描述

关闭文件:

#include <stdio.h>
int fclose(FILE *stream);

成功返回0,失败返回EOF(-1),并设置errno

流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘
当一个程序正常终止时,所有打开的流都会被关闭
fclose()函数的入参stream必须保证为非空,否则出现段错误。

2.2 数据的读取与写入

2.2.1 字符读
 #include  <stdio.h>
 int  fgetc(FILE *stream);
 int  getc(FILE *stream);   //宏
 int  getchar(void);
 //getchar();的效果等同于fgetc(stdin);

成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),不设置errno;

2.2.2 字符写
#include  <stdio.h>
int  fputc(int c, FILE *stream);
int  putc(int c, FILE *stream);
int  putchar(int c);
//putchar( c )等同于fputc(c, stdout)

成功时返回写入的字符;出错时返回EOF(-1)

2.2.3 行读
#include  <stdio.h>
char *gets(char *s);
char *fgets(char *s, int size, FILE *stream);

成功时返回s,到文件末尾或出错时返回NULL;
成功:遇到’\n’或已输入size-1个字符时返回,返回时自动加上’\0’

  • s,保存数据的地址;
  • size,最大读取字节数,如果读取的数据字节数大于等于size,只会读取size-1个字符到缓冲区s,最后添加’\0’,如果输入数据少于size-1 ,直接在最后添加换行符’‘\0’'。(直接回车让函数返回);

尽量不使用gets,容易造成缓冲区溢出;原因太基础了,我不愿意写;

2.2.4 行写
#include  <stdio.h>
int puts(const char *s);
int fputs(const char *s,  FILE *stream);

成功时返回输出的字符个数;出错时返回EOF(-1)
puts()将缓冲区s中的字符串输出到stdout,并追加’\n’使之输出;
fputs()将缓冲区s中的字符串输出到stream,不追加 ‘\n’;

2.2.5 二进制读

文本文件只能存储文本,二进制文件可以存储二进制数据,而所有的数据格式都可以转换为二进制格式;

#include  <stdio.h>
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);

成功返回读写的对象个数;出错时返回EOF(-1);
既可以读写文本文件,也可以读写数据文件,效率高;

  • void *ptr: 读取内容放的地址;
  • size_t size:读取的块大小;
  • size_t n: 读取块的个数;
  • FILE *fp: 读取的文件指针;
2.2.6 二进制写
#include  <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);

成功返回读写的对象个数;出错时返回EOF(-1);
既可以读写文本文件,也可以读写数据文件,效率高;

  • void *ptr: 写文件的内容的地址;
  • size_t size:写的块大小;
  • size_t n: 写的块个数;
  • FILE *fp: 要写的文件指针;

注意:文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。

2.2.7 流的刷新、定位、判断

流的刷新

#include <stdio.h>
int fflush(FILE *fp);

成功时返回0;出错时返回EOF(-1)
将流缓冲区中的数据写入实际的文件
Linux下只能刷新输出缓冲区,输入缓冲区丢弃
如果输出到屏幕使用fflush(stdout);
流的定位

long ftell(FILE *stream); //获取定位
/*定位到指定位置*/
long fseek(FILE *stream, long offset,  int whence); 
void rewind(FILE *stream); //定位到内容开头

fseek函数的whence参数:SEEK_SET;SEEK_CUR;SEEK_END;
SEEK_SET: 从距文件开头 offset 位移量为新的读写位置
SEEK_CUR:以目前的读写位置往后增加 offset 个位移量
SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量
offset参数:偏移量,可正可负
注意事项:
(1) 文件的打开使用a模式 fseek无效;
(2) rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
(3) 这三个函数只适用2G以下的文件;

检测流结束和出错

#include  <stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);

ferror():返回1表示流出错;否则返回0
feof():返回1表示文件已到末尾;否则返回0

2.2.8 很有用的格式化读写

格式化输出:

#include  <stdio.h>
int printf(const char *fmt,);
int fprintf(FILE *stream, const char *fmt,);
int sprintf(char *s, const char *fmt,);

成功时返回输出的字符个数;出错时返回EOF(-1);
fprintf:将格式化的字符串打印到文件;
sprintf:将格式化的字符存入字符类型的数组中;

格式化输入:

int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

成功时返回输出的字符个数;出错时返回EOF(-1);
作用与格式化输出相反;

三 文件IO

1 什么是文件IO

文件IO,又称系统IO,属于系统调用,是操作系统提供的API接口函数;
是根据posix(可移植操作系统接口)定义的一组函数;
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符;
访问各种类型文件;
Linux下, 标准IO基于文件IO实现;

2 文件描述符

fd(file descriptor),是0-1023的数字,表示文件。
文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。
文件描述符从0开始分配,依次递增。
文件IO操作通过文件描述符来完成。
0, 1, 2 表示标准输入,标准输出,错误;

3 编程接口

3.1 文件打开

 #include <fcntl.h>
 int open(const char *pathname, int flags);
 int open(const char *pathname, int flags, mode_t mode);

pen函数用来创建或打开一个文件:成功时返回文件描述符;出错时返回EOF(-1);
打开文件时可以只使用两个参数;
创建文件时第三个参数指定新文件的权限,(只有在建立新文件时有效)此外真正建文件时的权限会受到umask 值影响,实际权限是mode-umaks(umask 用来设定文件或目录的初始权限);
可以打开设备文件,但是不能创建设备文件;

  • pathname是要打开/创建的文件名(最好带路径);
  • flags
    在这里插入图片描述
    与标准IO对应:
    • r: O_RDONLY
    • r+: O_RDWR
    • w: O_WRONLY | O_CREAT | O_TRUNC, 0664
    • w+: O_RDWR | O_CREAT | O_TRUNC, 0664
    • a: O_WRONLY | O_CREAT | O_APPEND, 0664
    • a+: O_RDWR | O_CREAT | O_APPEND, 0664
  • mode指定新建文件的权限;

3.2 文件关闭

#include  <unistd.h>
int  close(int fd);

成功时返回0;出错时返回EOF(-1);
程序结束时自动关闭所有打开的文件;
文件关闭后,文件描述符不再代表文件;

3.3 文件写

#include  <unistd.h>
ssize_t  write(int fd, void *buf, size_t count);

成功时返回实际写入的字节数;出错时返回EOF(-1);
buf是发送数据的缓冲区;
count是写入的数据大小,**最好填buf处数据的实际大小;**不应超过buf大小;

3.4 文件读

 #include  <unistd.h>
 ssize_t  read(int fd, void *buf, size_t count);

成功时返回实际读取的字节数;出错时返回EOF(-1);读到文件末尾时返回0;
buf是接收数据的缓冲区
count是一次最多读取的大小,**应该写缓冲区大小,**不应超过buf大小

3.5 文件定位

#include  <unistd.h>
off_t  lseek(int fd, off_t offset, intt whence);

成功时返回当前的文件读写位置;出错时返回EOF(-1);
参数offset和参数whence同fseek完全一样;

四 目录文件的读取

1 打开目录

#include  <dirent.h>
DIR  *opendir(const char *name);
DIR  *fdopendir(int fd); 

成功时返回目录流指针;出错时返回NULL;
fdopendir使用文件描述符,要配合open函数使用;
name是文件名(最好带路径);
DIR是用来描述一个打开的目录文件的结构体类型;

2 读取目录

#include  <dirent.h>
struct  dirent *readdir(DIR *dirp);

成功时返回目录流dirp中下一个目录项;出错或到末尾时时返回NULL;
struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256];
也就是说,调用一次函数,就返回一个目录下的项,直到读完目录返回NULL;那么用while(readdir(mydrip));就可以读取完目录下的所有项;

3 关闭目录

#include  <dirent.h>
int closedir(DIR *dirp);

成功时返回0;出错时返回EOF(-1);

五 修改文件权限

#include  <sys/stat.h>
int  chmod(const char *path, mode_t mode);
int  fchmod(int fd, mode_t mode);

成功时返回0;出错时返回EOF(-1);
root和文件所有者能修改文件的访问权限;
示例: chmod(“test.txt”, 0666);

六 文件属性的获取

1 获取文件属性的函数:

#include  <sys/stat.h>
int  stat(const char *path, struct stat *buf);
int  fstat(int fd, struct stat *buf);
int  lstat(const char *path, struct stat *buf);

成功时返回0;出错时返回EOF(-1);

  • path是文件位置;
  • stat获取的是目标文件的属性;

lstat用于获取链接文件的属性;

2 属性分析

2.1 stat结构体

struct stat 
{
	dev_t         st_dev;    //文件的设备编号
    ino_t         st_ino;    //节点
    mode_t        st_mode;   //文件的类型和存取的权限
    nlink_t       st_nlink;  //连到该文件的硬连接数目,刚建立的文件值为1
    uid_t         st_uid;    //用户ID
    gid_t         st_gid;    //组ID
    dev_t         st_rdev;   //(设备类型)若此文件为设备文件,则为其设备编号
    off_t         st_size;   //文件字节数(文件大小)
    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;    //块数
    time_t        st_atime;     //最后一次访问时间
    time_t        st_mtime;     //最后一次修改时间
    time_t        st_ctime;     //最后一次改变时间(指属性)
};

例如st_mode可以用来判断文件类型和权限

2.2 判断文件类型

这里有几个宏函数,结果是"bool型",用于判断是否是某一类型;

S_IFMT                 0170000     文件类型的位遮罩
S_ISREG(st_mode)        0100000    是否常规文件
S_ISDIR(st_mode)        0040000    是否目录
S_ISCHR(st_mode)        0020000    是否字符设备
S_ISBLK(st_mode)        0060000    是否块设备
S_ISFIFO(st_mode)       0010000    是否FIFO文件
S_ISLNK(st_mode)        0120000    是否链接文件
S_ISSOCK(st_mode)       0140000    是否SOCKET文件

//示例:
if(S_ISREG(buf.st_mode)) //是否为该类型,
{
//是该文件类型会怎么样
}

2.3 判断文件权限

这里的几个宏表示,如果对应的位为1,那么就有对应的权限;

S_IRUSR         00400        bit:8    所有者有读权限
S_IWUSR         00200            7    所有者拥有写权限
S_IXUSR         00100            6    所有者拥有执行权限								  						 	
S_IRGRP         00040            5   群组拥有读权限
S_IWGRP         00020            4   群组拥有写权限
S_IXGRP         00010            3   群组拥有执行权限
S_IROTH         00004            2   其他用户拥有读权限
S_IWOTH         00002            1   其他用户拥有写权限
S_IXOTH         00001            0   其他用户拥有执行权限

可以在for循环中用if(buf.st_mode & (1<<i))来判断到底是哪个权限;

参考

修成真大佬的IO系列文章

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值