I/O
input&output,是一切实现的基础。如果没有i/o,我们在linux上编译的代码将只会在终端出现,将当前进程关闭后,数据无法保留。
标准IO与系统调用(文件)IO的区别
系统IO是操作系统封装的一些函数如open,close,printf,malloc等API函数构成,
简单来说标准IO由第三方规定,结合系统IO重新规划了一套C库函数,这些函数也是由系统IO封装实现的,但是在多平台下可以使用。
标准IO
不仅在 UNIX 系统,在很多操作系统上都实现了标准 I/O 库
标准 I/O 库由 ANSI C 标准说明
标准 I/O 库处理很多细节,如缓存分配、以优化长度执行 I/O 等,这样使用户不必关心如何选择合适的块长度
标准 I/O 在系统调用函数基础上构造的,它便于用户使用标准 I/O 库及其头文件 stdio.h 为底层 I/O 系统调用提供了一个通用的接口。
缓冲区
库函数的本质还是系统调用,只不过库函数多了缓冲区,缓冲区用于减少系统调用的次数
从而提高效率
只要调用标准 IO 相关的函数,都会创建缓冲区
标准 IO 既然有缓冲区来减少系统调用的次数,那什么时候会调用调用呢?只有当刷新缓冲
区的时候才会调用系统调用
文件指针
对于标准 IO 而言,任何一个打开的文件都具有很多属性,这些属性都保存在一个结构体里
面,这个结构体的类型是 FILE 类型,所以如果要操作文件,一般都需要一个 FILE 类型的指
针变量保存文件信息,所以对这个指针变量的操作就是对文件的操作,所以这个结构体指
针变量称之为文件指针,也称之为流指针
流(stream)
定义:所有的 I/O 操作仅是简单的从程序移进或者移出,这种字节流,就称为流。
分类:文本流/二进制流
****FILE 结构体: **********************************************************
typedef struct _IO_FILE FILE;
注意事项:
当一个程序运行的时候,系统会自动给当前程序分配三个文件指针,这三个文件指针
主要用于对终端的操作
stdin 标准输入流指针 从终端读取数据时要使用这个流指针
stdout 标准输出流指针 向终端写数据时要使用这个流指针
stderr 标准出错输出流指针 如果想终端写出错的信息,使用这个流指针
相关函数
- fopen() 打开或者创建文件,返回一个文件指针
- fclose() 关闭文件
- fprintf() 将参数写入文件
- fputc() 向文件写入字符
- fgetc() 从文件中读取一个字符
- fputs() 向文件写入字符串
- fgets() 从文件中读取字符串
- fread() 读取文件内容(任意类型的数据)
- fwrite() 向文件写内容
- fseek()/rewind()/ftell() 设置文件指针位置
- long ftell()(FILE*stream)
- void rewind(FILE*stream) 相当于fseek(fp,0,SEEK_SET)
- time() 得出从1970.1.1零点开始到当下的时间
函数使用
1.fopen()
头文件
:#include<stdio.h>
使用实例:
FILE *fopen(const char *pathname, const char *mode);
打开或创建文件
#include<stdio.h>
#include<errno.h>
int main()
{
FILE *fp;
//文件不存在就打开失败 并且设置错误码
fp = fopen("file.txt","r");//“r”表示以只读方式打开,定位到文件起始位置;
if(fp==NULL)
{
perror("fopen");
printf("errno = %d\n",errno);
return -1;
}
return 0;
}
r 以只读的方式打开文件,定位到文件起始位置
r+ 以读写的方式打开文件,定位到文件起始位置
w 以只写的方式打开文件,如果文件存在则清空,如果文件不存在则创建,定位到文
件起始位置
w+ 以读写的方式打开文件,如果文件存在则清空,如果文件不存在则创建,定位到文
件起始位置
a 以只写的方式打开文件,如果文件存在则追加,如果文件不存在则创建,定位到文
件末尾位置
a+ 以读写的方式打开文件,如果文件存在则追加,如果文件不存在则创建,定位到文
件末尾位置
返回值:
成功:返回一个文件指针;
失败:NULL,并设置错误码;
2.fclose()
头文件:
#include<stdio.h>
使用实例:
int fclose(FILE *stream);
关闭一个文件指针,关闭后无法通过文件指针对此文件进行操作
#include<stdio.h>
#include<errno.h>
int main()
{
printf("one:hello hua qing\n");
fclose(stdout);
printf("two :hello hua qing\n");
return 0;
}
返回值:
成功:0
失败:EOF
3.fprintf()
头文件:
#include<stdio.h>
使用实例:
int fprintf(FILE *stream, const char *format, …(arg));
将第二个参数的内容写入文件
stream:文件指针
format:同printf
arg:可变参,同printf
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp = fopen("file.txt","w");
if(fp==NULL)
{
perror("fopen");
}
printf("one:hello\n");//往终端打印数据
fprintf(stdout,"hi\n");//向stdout中打印数据
fprintf(fp,"My age is %d",30);//向stdout中打印数据
char buf[20] = {0};
// strcpy(buf,"hello");
puts("------------------------------");
sprintf(buf,"file%d",100);
printf("%s\n",buf);
char buf1[20] = "123456";
puts("------------------------------");
int num = atoi(buf1);
printf("num = %d\n",num);
return 0;
}
返回值:
成功:写入字节数;
失败:0
4.fputc()
头文件: #include<stdio.h>
使用实例:
int fputc(int c, FILE *stream);
向文件写入一个字符
c:将要写入的字符;
stream:文件指针;
#include<stdio.h>
int main()
{
// fputc('h',stdout);
// fputc('a',stdout);
// fputc(97,stdout);
FILE *fp;
if((fp = fopen("file.txt","w+"))==NULL)
{
perror("fopen");
return -1;
}
//写指针 会往后偏移
fputc('h',fp);
fputc('e',fp);
fputc('l',fp);
fputc('l',fp);
fputc('o',fp);
return 0;
}
返回值:
成功:要写入的字符;
失败:EOF
5.fgetc()
头文件
#include <stdio.h>
使用实例
int fgetc(FILE *stream);
从文件中读取字符。
stream:文件指针
#include<stdio.h>
int main()
{
// int a = fgetc(stdin);
// printf("a = %d a = %c\n",a,a);
FILE *fp;
if((fp=fopen("file.txt","r"))==NULL)
{
perror("fopen");
return -1;
}
#if 0
int vla;
printf("val = %c",val);
val = fgetc(fp);
printf("val = %c",val1);
val = fgetc(fp);
printf("val = %c\n",val2);
#endif
int val;
while((val = fgetc(fp))!=EOF)
{
printf("%c",val);
}
return 0;
}
返回值
成功:读取到的字符
失败:EOF
6. fputs()
头文件 #include<stdio.h>
使用实例
int fputs(const char *s, FILE *stream);
参数:
s:要写入的字符串;
stream:文件指针(指向要操作的文件);
#include<stdio.h>
int main()
{
//自带换行
// puts("-------------------------");
//不会自带换行
// fputs("hello\n",stdout);
int num;
FILE *fp;
if((fp = fopen("file.txt","w"))==NULL)//存在就会清空
{
perror("fopen");
return -1;
}
num = fputs("hello world",fp);
printf("num = %d\n",num);
return 0;
}
返回值
成功:返回一个非负值,默认为1
失败:EOF
7.fgets()
注意:
1)如果第二个参数设置的值 n 小于实际的字节数,只会保存前 n-1 个字节,最后一个位置
补\0
2)如果第二个参数设置的值 n 大于实际的字节数,会把换行符\n 也保存在字符串里面
(如何去除 buf 里面保存的换行符?将\n 变为\0 )
3)fgets 函数读取文件内容时,每次n个字节(取决于第二个参数),遇到行结束符\n 就会立即返回
例:
假设文件第一行有数据“hello world” 11个字节
当fgets第二个参数n设置大于 11 即读取时 会将换行符也读取进去 输出打印时 就会多个换行
此时strlen(buf)是12个字节
当fgets第二个参数n设置小于 11 即读取时 会读取到n-1个字节 留一个字节给尾0
头文件 #include <stdio.h>
使用实例
char *fgets(char *s, int size, FILE *stream);
从文件中读取字符串。
参数:
s:读取的内容
size:一次读取的字节数
stream:文件指针
#include<stdio.h>
#include<string.h>
int main()
{
char buf[20];
FILE *fp;
// fgets(buf,10,stdin);
// printf("buf = %s\n",buf);
if((fp = fopen("file.txt","r"))==NULL)
{
perror("fopen");
return -1;
}
fgets(buf,sizeof(buf),fp);
printf("buf = %s\n",buf);
fgets(buf,sizeof(buf),fp);
//解决多一个换行的原因
buf[strlen(buf)-1]= '\0';
printf("buf = %s\n",buf);
printf("strlen(buf) = %ld\n",strlen(buf));
return 0;
}
返回值
成功:若函数获取字符串放入数组中,则返回该数组的首地址指针;读完返回NULL;
失败:NULL
文件IO相关函数
- open()
- read()
- write()
- stat()
- opendir()/closedir()
- readdir()
- closedir()
1.stat()
stat() 函数可用于Unix/Linux系统中获取文件或目录的详细信息。它返回一个元组,其中包含有关文件或目录的细节,例如文件大小、修改时间、访问权限等。这些信息可以用于检查文件或目录的完整性,以及在文件系统中进行操作。
头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
使用实例
获取一个文件的信息
pathname:文件名
statbuf:获取的文件的信息结构体
stat结构体包含:
struct stat {
dev_t st_dev; /* 设备ID */
ino_t st_ino; /* inode节点号 */
mode_t st_mode; /* 文件得类型和模式 */
nlink_t st_nlink; /* 硬链接个数 */
uid_t st_uid; /* 当前用户id */
gid_t st_gid; /* 当前用户组*/
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 */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
};
The following mask values are defined for the file type:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket 套接字文件
S_IFLNK 0120000 symbolic link 链接文件
S_IFREG 0100000 regular file 普通文件
S_IFBLK 0060000 block device 块设备文件
S_IFDIR 0040000 directory 目录文件
S_IFCHR 0020000 character device 字符设备文件
S_IFIFO 0010000 FIFO 管道文件
总结:高两位是判断文件类型
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 当前用户可读可写可执行权限
S_IRUSR 00400 当前用户可读权限
S_IWUSR 00200 当前用户可写权限
S_IXUSR 00100 当前用户可行性权限
S_IRWXG 00070 用户组可读可写可执行权限
S_IRGRP 00040 用户组可读权限
S_IWGRP 00020 用户组可写权限
S_IXGRP 00010 用户组可执行权限
S_IRWXO 00007 others (not in group) have read, write, and
execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
总结:后三位是文件的权限
创建硬链 ln 源文件 目标文件
如果加上-s 选项 则创建软链接文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include<stdio.h>
int main()
{
struct stat mystat;
int ret;
ret = stat("test",&mystat);
if(ret==-1)
{
1 perror("stat");
return -1;
}
#if 0
printf("mystat.st_ino = %ld\n",mystat.st_ino);
printf("mystat.st_mode = %#o\n",mystat.st_mode);
printf("mystat.st_nlink = %ld\n",mystat.st_nlink);
#endif
switch(mystat.st_mode&S_IFMT)
{
case S_IFSOCK:
printf("s");
break;
case S_IFLNK:
printf("l");
break;
case S_IFREG:
printf("-");
break;
case S_IFBLK:
printf("b");
break;
case S_IFDIR:
printf("d");
break;
case S_IFCHR:
printf("c");
break;
case S_IFIFO:
printf("p");
break;
}
for(int n=8;n>=0;n--)
{
if(mystat.st_mode&(1<<n))
{
switch(n%3)
{
case 1:
printf("w");
break;
case 2:
printf("r");
break;
case 0:
printf("x");
break;
}
}
else
{
printf("-");
}
}
printf("\n");
return 0;
}
返回值
成功:0
失败:-1
2. 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_APPEND 文件存在则追加
O_TRUNC 文件存在则清空
O_CREAT 如果文件不存在则创建
O_EXCL 与 O_CREAT 一起使用,如果文件存在则报错
mode:指定创建文件的权限,一般使用八进制表示,0664
只有当 flags 有 O_CREAT 权限时才会指定当前参数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<stdio.h>
int main()
{
int fd;
//以只读的方式打开 文件不存在则报错
//fd = open("file.txt",O_RDONLY);
//以只读的方式打开 文件则创建 指定第三个参数值
fd = open("file.txt",O_RDONLY|O_CREAT,0664);
if(fd == -1)//创建失败返回一个错误(perror)
{
perror("open");
return -1;
}
//文件描述符会递增
printf("fd = %d\n",fd);
fd = open("file.txt",O_RDONLY|O_CREAT,0775);
printf("fd = %d\n",fd);
fd = open("file.txt",O_RDONLY|O_CREAT,0775);
printf("fd = %d\n",fd);
return 0;
}
返回值
成功:返回文件描述符
失败:-1
3. read()
头文件
#include <unistd.h>
使用实例
ssize_t read(int fd, void *buf, size_t count);
函数功能:读取文件信息
参数:
fd:文件描述符
buf:存放读到数据
count:读取多少字节
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd;
fd = open("file.txt",O_RDONLY|O_CREAT,0775);
if(fd == -1)
{
perror("open");
return -1;
}
char buf[32];
int ret;
ret = read(fd,buf,32);
printf("ret = %d buf=%s\n",ret,buf);
memset(buf,0,32);
//读到文件尾返回0
ret = read(fd,buf,32);
printf("ret = %d buf=%s\n",ret,buf);
return 0;
}
返回值
成功:返回读到的字节数
读到文件尾时返回 0
失败:-1
4. write()
头文件
#include <unistd.h>
使用实例
ssize_t write(int fd, const void *buf, size_t count);
功能:向文件写数据
参数:
fd:文件描述符
buf:要写入的内容
count:要写入的字节数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd;
fd = open("file.txt",O_WRONLY|O_CREAT|O_TRUNC,0775);
if(fd == -1)
{
perror("open");
return -1;
}
char buf[32] = "hello";
write(fd,buf,7);
return 0;
}
返回值
成功:返回写到的字节数
失败:-1
5. opendir()/closedir()
头文件
#include <sys/types.h>#include <dirent.h>
使用实例
DIR *opendir(const char *name);
功能:打开一个目录,返回一个目录流指针
参数:
name:目录名
返回值
成功:目录流指针
失败:NULL
closedir()
头文件
#include <sys/types.h>
#include <dirent.h>
使用实例
int closedir(DIR *dirp);
功能:关闭目录流指针
参数:
dirp:目录流指针,opendir 的返回值
返回值
成功:0
失败:-1
6. readdir()
头文件
#include <dirent.h>
使用实例
struct dirent *readdir(DIR *dirp);
功能:获取目录中文件的信息
参数:
dirp:目录流指针,opendir 的返回值
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#if 0
struct dirent
{
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
#endif
int main()
{
DIR *dir;
struct dirent*mydirent;
if((dir = opendir("test"))==NULL)
{
perror("opendir");
return -1;
}
#if 0
mydirent = readdir(dir);
printf("mydirent->d_ino = %ld\n",mydirent->d_ino);
printf("mydirent->d_name = %s\n",mydirent->d_name);
//运行一次就读取一个文件信息
mydirent = readdir(dir);
printf("mydirent->d_ino = %ld\n",mydirent->d_ino);
printf("mydirent->d_name = %s\n",mydirent->d_name);
#endif
while(mydirent = readdir(dir))
{
printf("d_ino = %ld d_name = %s\n",mydirent->d_ino,mydirent->d_name);
}
return 0;
}
返回值
成功:保存文件信息的结构体
失败:NULL
如果目录信息读取完毕,返回 NULL
7. closedir()
头文件
#include <sys/types.h>
#include <dirent.h>
使用实例
int closedir(DIR *dirp);
功能:关闭目录流指针
参数:
dirp:目录流指针,
返回值
成功:0
失败:-1