Linux文件IO-系统IO和标准IO的接口函数

系统IO

linux设计哲学
everything is a file in linux
也就是说在linux下面,操作任何东西都是在操作文件。或者说在Linux下面操作任何东西就是通过文件
的接口去实现的。

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

linux文件操作的原理

文件
文件属性 i-node 唯一标识文件存在与否
文件内容

文件操作过程:
硬件
i-node ->文件内容
系统识别一个文件存在就会创建一个struct inode{}来管理文件的信息
struct file用来描述打开一个文件
操作文件的过程 struct file -》 struct inode —》硬件上的i-node—>文件的内容
linux为了屏蔽文件操作的具体细节,他为每一个进程创建了一个文件表项来保存每个进程打开的文件。

系统IO的接口函数

1.open函数

功能:用于打开或者创建一个文件
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
函数原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数:
@pathname:字符串类型 表示你要打开哪个文件,一般建议加上路径
@flags:整数 打开的标志,表示以什么样的形式打开文件
标志分为可选和必选:
以下三个标志必选其一:
O_RDONLY 只读打开文件
O_WRONLY 只写打开文件
O_RDWR. 可读可写
除了以上的必选标志,还能以位或(|)的形式组合一些可选标志,
O_CREAT 如果文件不存在,则创建它
O_APPEND 把写入的数据追加到文件末尾
O_NONBLOCK 以不阻塞的方式打开文件(默认是阻塞的)
@mode:整数 表示权限,用于创建文件,指定文件的权限
rwx --x r-x
111 001 101 ==> 0715
返回值:
整数 失败返回-1,同时设置errno
errno是一个全局(整数)变量,称为错误码,在头文件<errno.h>定义了该变量,害
定义了一组与之相关的错误码常量。
当系统函数调用失败的失败,会给errno设置一个大于0的值,
系统提供了两个函数用来获取错误的原因:
#include <stdio.h>
void perror(const char *s);
先输出字符串s,在输出一个:在输出一个空格 最后就是具体的错误信息。
#include <string.h>
char *strerror(int errnum);
根据给定的errno的值,返回一个对应的错误码的信息
成功返回一个新的文件描述符
文件描述符什么?
文件描述符是一个大于2整数,它实际上就是那个文件表项数组的下标
当打开或者创建一个文件的时候,内核就会返回一个新的文件描述符。
我们新打开一个文件,它的文件描述符一般是从3开始的
当程序运行的时候,系统会自动打开三个文件,分别是:
标准输入 0 一般指键盘
标准输出 1 一般指屏幕
标准错误 2 一般指屏幕

2.read函数

功能:用于从打开的文件(描述符)中读取数据
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
@fd:文件描述符 表示从哪个文件中读取数据
@buf:数据缓存区 表示读到的数据放在哪里
@count:你要读多少个(字节)
返回值:
失败返回-1,同时设置errno
成功则返回实际读到的字节数
如果返回0,表示到达文件末尾(读完了)

3.write函数

功能:用写入数据到打开的文件中(描述符)
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
参数:
@fd:文件描述符
@buf:数据缓存区,表示我要把buf里面的内容写入到fd这个文件中
@count:表示要写多少个(字节)
返回值:
失败返回-1,同时设置errno
成功返回写入的字节数

4.close函数

功能:关闭一个文件
头文件:
#include <unistd.h>
函数原型:
int close(int fd);
参数:
@fd:文件描述符
返回值:
成功返回0
失败返回-1,同时设置errno

5.lseek函数

功能:重定位 读/写文件的偏移量(用于移动文本光标的位置)
头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
@fd:文件描述符
@offset:移动的偏移量 可正可负,正数表示往右边偏移,负数表示往左边偏移
@whence:参照位置
SEEK_SET 文件开头的位置
SEEK_CUR 文件当前的位置
SEEK_END 文件末尾的位置
返回值:
成功返回从文件开头到当前位置的偏移量
失败返回-1,同时设置errno

例如
求一个文件的大小

int main(int argc, char const *argv[])
{
    
    
//以可读可写的形式打开./1.txt,如果没有则创建 并给权限0644
int fd = open("./1.txt",O_RDWR | O_CREAT,0644);
if(-1 == fd)//如果失败fd会变成-1,同时errno会被设置
{
    
    
//这两种二选1
//perror("open 1.txt error");
printf("open 1.txt error :%d %s\n", errno,strerror(errno));
return 1;
}
//求文件大小
int r = lseek(fd,0,SEEK_END);
if(-1 == r)
{
    
    
perror("lseek error");
return 1;
}
printf("该文件的大小为%d\n",r);
return 0;
}

实现cp这个功能

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include<errno.h>
#include<string.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
void mycp(char const * src_file, char const * dest_file)
{
    
    
//以只读的方式打开源文件
int src_fd = open(src_file,O_RDONLY);
if(-1 == src_fd)
{
    
    
perror("open src error");
exit(1);//结束这个进程
}
//以只写的方式打开目标文件,如果没有则创建
int dest_fd = open(dest_file,O_WRONLY | O_CREAT,0664);
if(-1 == dest_fd)
{
    
    
perror("open dest error");
exit(1);
}
char buf[1024] = {
    
    0};
int r;
while(1)
{
    
    
r = read(src_fd,buf,1024);
if(-1 == r)
{
    
    
perror("read error");
}
if(0 == r)//读完了
{
    
    
break;
}
write(dest_fd,buf,r);//每次读到多少个字节就写多少个字节
}
//关闭目标文件和源文件
close(src_fd);
close(dest_fd);
}
int main(int argc, char const *argv[])//mycp ./1.txt /home/china/2.txt
{
    
    
//判断参数是否合法
if(argc != 3)
{
    
    
printf("Usgae : %s <src_filename> <dest_filename>\n",argv[0]);
return 1;
}
//拷贝文件
mycp(argv[1],argv[2]);//argv[1] --> cp-->argv[2]
return 0;
}

标准IO

标准IO和系统IO
系统IO的特点:
通用,普通文件 管道文件 …套接字 都可以使用系统IO
函数简单,不带缓冲

标准IO的特点:
跨平台,只要能跑c语言,标准IO就可以使用
读写的方式更丰富
一次性读写一个字节
一次性读写一行字节

读写效率更高
使用标准IO,数据会先被存储,满足条件以后才能写入内核。

标准IO的缓冲
标准IO中提供三种缓冲:
全缓冲:
在这种情况下,需要把这个缓存区填满或者手动刷新或者关闭文件 才能进行实际的IO操作

行缓冲:
在这种情况下,当输入遇到换行时\n或者手动刷新或者关闭文件 才能进行实际的IO操作

int main()
{
    
    
printf("abc\n");
while(1);
}

无缓冲:
在这种情况下,标准IO不缓存数据,直接进行IO操作

int main()
{
    
    
perror("abc");
while(1);
}

流与对象

使用标准IO的方式与使用系统IO类似
都是先打开一个文件建立从用户空间到内核空间的访问路径
然后把打开操作的返回值,作为其他函数的参数。
只不过系统IO,所有的IO操作都是围绕着文件描述符。
在标准IO中,也有一个类似与文件描述符的概念,我们称之为流(stream),这个流实际上就是指向结构体FILE的指针
结构体FILE其实就是对文件描述符,和读写缓存区的封装

接口说明

打开和关闭流

功能:打开文件流
头文件:
#include <stdio.h>
函数原型:
FILE *fopen(const char *pathname, const char *mode);
参数:
@pathname: 你要打开文件的名字 建议路径
@mode: 打开的模式
r 以只读的方式打开文本文件,文本的光标位于文件开始的位置
r+ 以读写的方式打开文件文件. 文本的光标位于文件开始的位置
w 以写的方式打开这个文件,如果存在则清空,不存在则创建,文本的光标位于
文件开始的位置
w+ 以读写的方式打开文件. 如果存在则清空,不存在则创建,文本的光标位于
文件开始的位置
a 以追加写的方式打开文件(写数据到末尾)文件不存在则创建 ,文件的光标
位于文件末尾
a+ 以读和追加写的方式打开文件如果不存在则创建。
如果是读则光标在文件开头,如果是写,则总是追加到文件末尾
┌─────────────┬───────────────────────────────┐
│fopen() mode │ open() flags │
├─────────────┼───────────────────────────────┤
│ r │ O_RDONLY │
├─────────────┼───────────────────────────────┤
│ w │ O_WRONLY | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a │ O_WRONLY | O_CREAT | O_APPEND │
├─────────────┼───────────────────────────────┤
│ r+ │ O_RDWR │
├─────────────┼───────────────────────────────┤
│ w+ │ O_RDWR | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a+ │ O_RDWR | O_CREAT | O_APPEND │
└─────────────┴───────────────────────────────┘
O_TRUNC :如果文件存在,则清空
返回值:
成功FILE类型的指针,这个指针就是代表你要操作的哪个文件
失败返回NULL,同时设置errno
在程序启动的时候,同样有3个文件流被自动打开,
分别是
标准输入流 stdin 0
标准输出流 stdout 1
标准错误流 stderr 2
功能:关闭文件流
#include <stdio.h>
int fclose(FILE *stream);
返回值:
成功返回0,失败返回-1,同时设置errno

读写文件块

功能:用来从stream指定的文件读/写nmemb的对象,且每个对象大小为 size读/写到ptr
头文件:
#include <stdio.h>
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
@ptr 首地址,表示读出来的数据放到那里
@size 每个对象的大小
@nmemb 读到几个对象
@stream:文件流 表示从哪个文件读
返回值:
成功返回实际读到的对象数
失败返回-1,同时设置errno
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数:
@ptr 首地址 数据的来源
@size 每个对象的大小
@nmemb 写入几个对象 n*size
@stream 文件流 表示写入哪个文件
返回值
成功返回实际写入的对象数
失败返回-1,同时设置errno

读写一个字节

#include <stdio.h>
int fgetc(FILE *stream);//从指定的文件流中获取下一个字符,返回一个该字符的int形式
int getc(FILE *stream);//与fgetc一样的功能,只不是getc是宏定义实现的(宏的效率会更高)
int getchar(void); //---》等价fgetc(stdin) 从标准输入中获取一个字符
#include <stdio.h>
int fputc(int c, FILE *stream);//把指定字符c写入stream指定的文件中
int putc(int c, FILE *stream);
int putchar(int c); //把指定的字符写入到stdin ===》fputc(c,stdout);

读写一行

#include <stdio.h>
char *gets(char *s);//从终端获取一个以换行符结束的字符串,保存在s指向的空间里面
//从指定的文件流中,最多读取size-1个字符存放到s指向的空间
//如果读到了\n也会保存在缓存区 不过有没有读到\n都会在最后吗补一个\0用来表示结束字符串
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);//把s指定的字符串写入到stream指定的文件
流中
int puts(const char *s);//等价于fputs(s,stdout); puts会自动换行

格式化IO

#include <stdio.h>
//按照格式输出数据 ---》stdout
int printf(const char *format, ...);
//按照格式输出数据 ---》stream指向的文件流中
int fprintf(FILE *stream, const char *format, ...);
//按照格式输出数据 ---》str指向地址
int sprintf(char *str, const char *format, ...);
多了一个大小的限制,
int snprintf(char *str, size_t size, const char *format, ...);
//从标准输入stdin 格式化输入数据
int scanf(const char *format, ...);
//从stream指定的文件流中输入格式化输入数据
int fscanf(FILE *stream, const char *format, ...);
//从s指定的文件流中输入格式化输入数据
int sscanf(const char *str, const char *format, ...);

定位流

#include <stdio.h>
//fseek功能和lseek类似,区别是
//lseek的返回值是返回相对于文件开始的偏移量
//fseek的返回值是0或者-1
int fseek(FILE *stream, long offset, int whence);
//用于返回文本光标的相对于文件开头的位置的偏移量大小
long ftell(FILE *stream);
//把文件的光标移回到文件开头的位置
void rewind(FILE *stream);

刷新流

#include <stdio.h>
int fflush(FILE *stream);
使用fflush 刷新缓存区的数据,直接写入文件
int main(int argc, char const *argv[])
{
    
    
printf("abc");
fflush(stdout);
while(1);
return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46836491/article/details/126329362