标准IO:
只能操作普通文件和管道文件
拥有缓存区
属于库函数调用
可移植性不同,可以移植到windows或者其他支持c语言的系统中使用
使用场景不同,有缓存区,运行效率高,使用在需要高效率运行的场景(针对数据库的大量访问)
句柄不同,使用的FILE*的文件流指针来指向文件
文件IO:
可以操作绝大部分文件
没有缓存区
属于系统调用
可移植性不同,只能在类unix系统中使用
使用场景不同,没有缓存区,运行效率低,但是实时性高,使用在实时性高的场景(网络通信)
句柄不同,使用的int类型的文件描述符来指向文件
缓存区
为了提高IO的运行效率,将多次准备写入文件中的数据,先统一的写入一段内存进行打包,然后将这段内存统一写入文件中,这样可以大量的减少文件的打开和关闭操作。
缓存区刷新(缓存区打包完成开始执行写入操作)
缓存区有三个不同的类型:行缓存,全缓存,无缓存
行缓存(printf):一定是指向终端的stdout,就是行缓存
行缓存大小为1024字节
- 遇到回车(\n)刷新
- 缓存区满刷新
- 程序结束刷新
- 文件关闭缓存刷新
- IO切换缓存刷新
- fflush(stdout)手动缓存
全缓存:通过fopen打开的指向文件的FILE*指针,都是全缓存
全缓存大小为4096字节
- 缓存区满刷新
- 程序结束刷新
- 文件关闭缓存刷新
- IO切换缓存刷新
- fflush(stdout)手动缓存
无缓存:标准错误流都是无缓存
scanf的缓存区
scanf实际上是从缓存区读取数据
每个scanf后面跟一个while(getchar!=10);
FILE结构体
结构体中一些关键数据如下
struct _IO_FILE {
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area.缓存区的首地址 */
char* _IO_buf_end; /* End of reserve area.缓存区的尾地址 */
int _fileno; //文件描述符
};
我们可以使用缓存区的尾地址 - 缓存区的首地址 得到 缓存区的大小
注意,在计算缓存区大小之前,需要使用一下
系统调用函数
man手册中系统调用函数都是在2中(man 2 需要查找的系统函数)
man手册中库函数都是在3中(man 3 需要查找的库函数)
linux系统提供给用户的一种函数
功能:能够让用户调用用户无法直接调用的系统底层函数
为什么printf不是系统调用
printf只是c语言库提供的函数
printf能在支持c语言语法的windows系统中使用
write是linux系统提供的函数
write函数绝对不能在windows系统中运行
int类型的描述符是如何与文件地址相关联的
获取int类型的描述符
open:
open产生的描述符最小值为三(0,1,2分别被标准输入流,标准输出流,标准错误流占用了)
描述符产设的规则为最小未使用
函数原型:
int open(const char *pathname, int flags, mode_t mode);
调用形式:
int fd =open(“文件,名字”,O_RDONLY | O_TRUNC | O_CREAT,0666);//以只读形式打开
int fd =open(“文件,名字”,O_WRONLY | O_TRUNC | O_CREAT,0666);//以只读形式打开
功能:
打开pathname文件,以falgs属性打开,文件成功打开,返回文件描述符
参数pathname:准备打开的文件的路径
参数flags:文件打开的属性集合,主要有以下几种常用属性
O_RDONLY:以只读形式打开文件
O_WRONLY:以只写形式打开文件
O_RDWR:以读写形式打开文件
O_TRUNC:如果文件存在,则清空文件内容后打开
O_CREAT:如果文件不存在,则创建文件打开
O_APPEND:以追加写的形式打开
O_EXCL:如果文件存在,则打开失败,一般配合O_CREAT使用,表示如果文件不存在创建文件,如果文件存在则啥也不干,主要为了确保文件存在
O_NONBLOCK:以非阻塞的形式打开文件(阻塞:条件不满足则不打开(scanf:若缓存区没有数据读取则挂起等待输入数据))
fopen(“文件名”,“w”)---> open ("文件名",O_WRONLY | O_TRUNC | O_CREAT)
属性的叠加需要用“|”而不是“+”
属性的去除,用“&”(~想要去除的数据)
参数mode:仅当第二个参数flags里存在O_CREAT的时候,才需要传入参数mode
用来表示:以mode权限来创建文件,所以一般mode会写成0664或者0666
close
函数原型
int close(int fd);
write和read函数
write:数据流
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
调用形式:
write(fd,任意数据的地址,数据大小)
功能:
将buf指向的数据中,最多count个字节的数据,写入fd所表示的文件中去。
- 最多count个字节是因为:write不仅仅能够将数据写入普通文件,还能写入套接字文件,套接字文件有大小限制,如果文件剩余内存大小小于想要写入的数据大小,则多余数据丢失
参数fd:文件描述符
参数buf:任意类型的数据的地址
参数count:想要发送的数据大小
返回值:成功写入返回写入的字节的字节数,返回0表示没有数据写入,写入失败返回-1(一般因为设备本身的原因)
read:
函数原型:
ssize_t read(int fd, void *buf, size_t count);
调用形式:
int a=read)(描述符,存放数据的地址,最多读取数据的数量);
功能:
从fd描述的文件,读取最多count个字节的数据,然后将数据写入到buf指向的内存中
参数fd:文件描述符
参数buf:用来存放读取到的数据的地址
参数count:最多读取count个字节的数据
返回值:读取成功,返回成功读取的数据的字节数,返回0表示未读取到数据,读取失败返回-1(基本都是因为文件本身破损的原因导致的读取失败)
3个特殊的描述符
标准输入流:STDIN_FILENO
标准输出流:STDOUT_FILENO
标准错误流;STDERR_FILENO
重定向
dup2
函数原型
int dup2(int oldfd, int newfd);
函数使用
int fd=open(“错误日志”);
dup2(fd,STDERR_FILENO);将标准错误流重定向到fd
dup2(fd2,fd1);
功能:
将newfd重定向到oldfd
参数oldfd:重定向的目标描述符
参数newfd:重定向的原描述符
dup
函数原型:
int dup(int oldfd);
调用形式:
int b=dup();
功能:
将oldfd描述的文件,在返回值(b)处做备份
练习
将一个程序的标准输出流重定向到文件msg.txt中,将标准错误流重定向到err.txt中并测试
printf(“hello\n”);
printf(“world\n”);
要求:将hello输出到msg.txt中,world输出到终端
int main(int argc, const char *argv[])
{
int fd1 = open("./msg1.txt",O_WRONLY | O_TRUNC | O_CREAT,0666);
int fd2=dup(STDOUT_FILENO);
dup2(fd1,1);
printf("hello\n"); //此时stdout是流向msg1.txt中,printf由行缓
//存变为全缓存,全缓存遇到\n不会刷新缓存
fflush(stdout); //手动刷新缓存
dup2(fd2,1);
printf("world\n");
close(fd1);
return 0;
}
获取文件权限和属性的函数
access
函数原型:
int access(const char *pathname, int mode);
调用形式:
int a=access(文件名,F_OK/R_OK/W_OK/X_OK);
if(0==a)
{
判断成功
}
功能:
判断pathname文件是否存在或者是否用有mode权限
参数pathname:文件的路径名
参数mode:判断文件权限的选项
F_OK:判断文件是否存在
R_OK:判断文件是否有用户的可读权限
W_OK:判断文件是否有用户的可写权限
X_OK:判断文件是否有用户的可执行权限
返回值:如果判断成功(拥有权限或者文件返回),返回0,判断失败返回-1
stat/fstat/lstat
函数原型
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
这三个函数全是用来实现获取文件属性以及文件的类型
stat与fstat的区别:
stat用的是文件名。
fstat用的是文件描述符。
stat与lstat的区别:
stat函数判断软链接文件的时候,会找到被软连接文件所链接的原文件去判断类型。
lstat判断软链接文件直接判断为软连接文件。
功能:
获取文件的属性,并将获取到的文件的属性存入statbuf指向的结构体中
statbuf结构:
struct stat {
dev_t st_dev; /* ID of device containing file *
/
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
};
其中核心数据就是 mode_t类型的 st_mode 结构体变量
st_mode里面就存放了文件类型以及文件的属性
权限
The following mask values are defined for the file mode component of the st_mode field:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
//属主的权限user
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
//所在组的权限group
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
//other的权限
S_IRWXO 00007 others (not in group) have read, write, and exe‐
cute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
掩码
文件创建的时候最终权限=手动指定的权限 去除 掩码权限
去除:&(~需要去除的数据)
某些程序权限过高无法运行,需要通过掩码来降低权限。
举例:
掩码设定成的是 0111
文件创建的时候,指定的权限是 0777
最终文件权限是 0666
如文件创建的时候,指定的权限是0666 //权限中没有0111所以不需要去除
最终文件权限是 0666
如果文件创建的时候,指定的权限是 0333
最终文件权限是 0222
umask*
函数原型:
mode_t umask(mode_t mask);
功能:
将当前程序的掩码设置成mask,那么当前程序创建的所有文件都会去除mask值
可变参函数
函数的参数列表中为(固定参,...)其中...为可变参
可变参的地址跟在固定参后面
练习
使用read函数读取一个文件中的所有内容
使用printf函数将读取到的内容,写入到另一个文件中
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
int main(int argc, const char *argv[])
{
int fd1 = open("./read",O_RDONLY);
int fd2 = open("./read1",O_WRONLY | O_TRUNC | O_CREAT,0666);
dup2(fd2,STDOUT_FILENO);
while(1)
{
char arr[24]={0};
int a = read(fd1,arr,23);
if(0==a)
break;
printf("%s",arr);
fflush(stdout);
}
return 0;
}
使用stat函数判断一个文件是否拥有用户可写,同组人可写,其他组人可写权限
如果同时拥有以上三个权限,则关闭上述三个权限(使用chmod函数)如果以上三个权限不全,则补全
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
int main(int argc, const char *argv[])
{
struct stat buf={0};
stat(argv[1],&buf);
mode_t mode =buf.st_mode;
if((mode | S_IWUSR )==mode && (mode | S_IWGRP )== mode && (mode | S_IWOTH )== mode)
{
chmod(argv[1],0444);
}
else
{
chmod(argv[1],0664);
}
return 0;
}