目录
一、带参数的main函数
带参main函数的格式
带参main的示例
二、文件操作
1、文件结构
2、文件操作的方式
3、文件IO和标准IO区别
①概念介绍
②主要区别
三、文件IO
1、特性
2、操作流程
3、相关函数
open
close
write
read
lseek
4、综合示例
四、标准IO
1、特性
①文件ID
②缓冲区
2、操作流程
3、相关函数
①与文件IO对应的标准IO
fopen
fclose
fwrite
fread
fseek
②标准IO的其他函数
fprintf
fscanf
fgetc
fputc
fgets
fputs
feof
fflush
ftell
总结
Linux下一切皆为文件.
在了解文件操作之前,需要先了解程序的入口--->带参数的main函数
一、带参数的main函数
为什么main函数要带参数呢?
因为要给main函数传递参数,需要进行合法性判断。
Linux系统中的大多数命令都位于/bin或/sbin目录下,这是因为这些目录是系统用来存放基本命令和系统管理命令的标准位置。然而,用户也可以在其他位置安装命令,只要这些位置被包含在环境变量PATH中,系统就能够找到并执行它们。
带参main函数的格式
main函数的原型:
int main(int argc,char *argv[]);
作用:
C和C++程序中主函数的一种常见声明方式,用于程序的入口点
标准格式进行解释:这有两个参数,这些参数通常用于处理命令行,输入运行时候生效
参数:
int 表示 main 函数返回一个整数,通常用于指示程序的退出状态。按照惯例,返回 0 表示程序成功执行,返回非零值表示出现了某种错误或异常情况。
argc(Argument Count的缩写):运行时参数个数(注意:包含可执行程序本身),至少为1,因 为第一个命令行参数总是程序的名称
argv(Argument Vector的缩写):字符指针数组,运行时各个参数指针(就是识别字符串),用于存储每个命令行参数的字符串。argv[0]是程序的名称,argv[1]是第一个参数,依此类推。argv[argc] 是NULL,用来标记数组的结束。
注意:这个定义是标准的,并且被编译器和链接器所理解。当你编写一个 C 或 C++ 程序时,你可以直接使用这个定义来接收命令行参数,而无需自己实现任何额外的逻辑来解析命令行。
例子:
一个程序名为example,如果在命令行中这样运行它:./example.out arg1 arg2
argc的值将是3,因为有三个参数:example(程序名),arg1,和arg2。
argv[0]将是字符串"./example"(或程序的完整路径,如果提供了完整路径)。
argv[1]将是字符串"arg1"。
argv[2]将是字符串"arg2"。
argv[3]将是NULL。
通过这种方式,程序可以读取并处理这些参数,以执行不同的操作或修改其行为。
带参main的示例
示例1:验证一下带参数的main函数 ./a.out hello world whl
现象:
代码:
#include<stdio.h>
int main(int argc,char *argv[])
{int i;//输出对应的argc printf("argc=%d\r\n",argc);//输出对应的argvfor(i = 0;i < argc;i++){printf("argv[%d]=%s\r\n",i,argv[i]);}return 0;
}
示例2:利用main函数的参数,实现计算机的效果 ./a.out 11 + 22
分析思路:1、可以使用argv[0]=.a/a.out argv[1]=11 argv[2]=22
2、分析发现都是字符--->字符串转整型-->atoi函数
3、利用选择结构分别输出+-*÷的结果
说明:
atoi
函数功能:
主要用于将字符串转换成整数。
头文件:
#include <stdlib.h>
函数原型:
int atoi(const char *nptr);
函数参数:
const char *nptr:nptr指向的字符串中提取第一个数字(识别转换符号-->数字、空白字符和正负号),并将识别的数字返回。特殊实例:char a[]=123a 使用atoi(a)--->执行完成之后只能识别123。也就是atoi函数在遇到非数字字符(如运算符-->除+-外)时停止转换,并返回已转换的整数部分。
函数返回值:
atoi函数的返回值为int类型的整数,转换后的值不可超出int可表示的范围。
现象:
代码:
#include<stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{int num1,num2;//将字符串转为整数num1=atoi(argv[1]);num2=atoi(argv[3]);//计算方式选择switch(*argv[2]){case '+':printf("sum=%d\r\n",num1+num2);break;case '-':printf("sum=%d\r\n",num1-num2);break;case '*':printf("sum=%d\r\n",num1*num2);break;case '/':printf("sum=%f\r\n",(float)num1/num2);break;}return 0;
}
二、文件操作
1、文件结构
Linux 系统不同于 Windows,没有 C 盘、D 盘、E 盘那么多的盘符,只有一个根目录(/),所有的文件(资源)都存储在以根目录(/)为树根的树形目录结构中。
在 Linux 根目录(/)下包含很多的子目录,称为一级目录。例如 bin、boot、dev 等。
同时,各一级目录下还含有很多子目录,称为二级目录。例如 /bin/bash、/bin/ed 等。
bin:bin是Binary的缩写, 这个目录存放着最经常使用的命令。
boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。
dev:dev是Device(设备)的缩写, 该目录下存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。
etc:所有的配置文件, 所有的系统管理所需要的配置文件和子目录都存放在这里。
home:用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。
root:该目录为系统管理员(也称作超级管理员)的用户主目录。
lib:这个目录里存放着系统最基本的动态连接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。
usr:系统用户工具和程序
– bin:用户命令
– sbin:超级用户使用的比较高级的管理程序和系统守护程序。
– include:标准头文件
– lib:库文件
– src:内核源代码
tmp:用来存放一些临时文件。
media:linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,linux会把识别的设备挂载到这个目录下。
mnt:临时挂载其他文件。
proc: 包含了进程的相关信息。
var:存放着在不断变化的文件数据,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。
2、文件操作的方式
文件是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位。文件的操作核心就是读和写,即用户或应用程序通过操作系统向硬盘发起读写请求,操作系统将这些请求转换成具体的硬盘指令,从而实现对文件的读取和写入。对相应的文件进行操作,这个的操作过程是操作相关的函数---需要使用到对应的API函数。
根据文件的操作平台(linux)的不同,给文件操作进行分类,主要分为两大类:第一类:文件IO(系统调用接口) 第二类:标准IO(#include<stdio.h>)
3、文件IO和标准IO区别
①概念介绍
标准IO
标准IO,也称为高级IO,是C语言标准库提供的一种IO操作方式。它使用文件流(如stdin、stdout、stderr)作为操作入口,通过缓冲机制(全缓冲、行缓冲、不缓冲)来管理数据的输入和输出。标准IO提供了丰富的函数接口,如fopen、fclose、fread、fwrite等,方便开发者进行文件操作。
文件IO
文件IO,也称为低级IO,是Linux系统调用提供的一种IO操作方式。它通过文件描述符来访问文件,文件描述符是一个非负整数,用于标识打开的文件。文件IO没有缓冲机制,直接对文件进行读写操作。常见的文件IO函数有open、close、read、write等。
②主要区别
缓冲机制
标准IO具有缓冲机制,可以自动处理数据的缓冲和刷新,提高了IO效率。而文件IO没有缓冲机制,需要开发者手动管理数据的读写。
函数来源
标准IO的函数接口来自于C语言标准库,与操作系统无关,因此具有良好的移植性。而文件IO的函数接口来自于Linux内核,与操作系统紧密相关,移植性较差。
操作入口
标准IO操作文件的入口是文件流,通过文件流来进行数据的读写。而文件IO操作文件的入口是文件描述符,通过文件描述符来访问文件。
所处位置
文件IO处于应用层与内核层之间,而标准IO处于应用层内。
三、文件IO
1、特性
用文件IO的方式读写文件的前提是需要获取到linux系统分配一个文件描述符fd。
那什么是文件描述符呢?
文件描述符用于标识和访问操作系统中某个被打开的文件或I/O资源。通过文件描述符,进程可以执行对相关资源的各种操作,如读取数据、写入数据、关闭文件或获取/改变其属性等。
关于文件描述符又可以分为两类:
第一类:文件IO特殊类型—特殊情况
当一个程序开始运行时,它一般会有3个已经打开的文件描述符,就是
0:标准输入->stdin-->对应的文件标识符 fd--文件的标识符0 –键盘
1:标准输出->stdout-->对应的文件标识符 fd--文件的标识符1—屏幕
2:标准错误->stderr-->对应的文件标识符 fd--文件的标识符2 –错误
第二类:文件IO的一般特性
也可以称为非特殊类型,主动调用open()分别打开不同文件来获得对应的文件描述符(随机分配)--fd,文件描述符和文件是绑定在一起,并且具有唯一性,持续到调用close(fd),相应的程序运行就结束了。
2、操作流程
文件IO的操作过程:
打开文件->操作文件->收回文件
首先,先用open函数操作文件,根据实现的功能对文件进行操作write()、read(),lseek(),对文件进行操作,完成需要使用close(fd)。
关键点:对文件进行读写操作
3、相关函数
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);
函数参数:
const char *pathname:来传递对应文件的路径
int flags:
权限标识O_RDONLY(只读), O_WRONLY(只写), O_RDWR(可读可写)
文件标识:
O_CLOEXEC: 当使用exec系列函数执行新程序时,设置此标志会使文件描述符自动关闭。这有助于防止文件描述符在不需要时被意外继承,从而提高安全性。
O_CREAT: 如果指定的文件不存在,则创建该文件。通常与mode参数一起使用,以指定新文件的权限。
O_DIRECTORY: 如果路径名不是一个目录,则open调用失败。这用于确保操作仅针对目录进行。
O_EXCL: 与O_CREAT一起使用时,如果文件已存在,则open调用失败。这通常用于创建文件时确保文件名的唯一性。
O_NOCTTY: 如果打开的是一个终端设备,则不将该设备设置为控制终端。这可以防止通过打开终端设备而意外地改变进程的控制终端。
O_NOFOLLOW: 如果路径名是一个符号链接,则open调用失败。这有助于防止通过符号链接进行的安全漏洞,例如目录遍历攻击。
mode_t mode:文件权限
函数返回值:
对应文件的文件描述符fd
示例:利用open函数,打开对应的文件并且创建相应的文件,输出fd观察一下
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{int fd;//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success");}return 0;
}
close
函数功能:
关闭文件,并收回对应的文件描述符
头文件:
#include <unistd.h>
函数原型:
int close(int fd);
函数参数:
int fd:文件描述符
函数返回值:
成功 返回0
失败 返回-1
示例:利用close函数,关闭对应的文件fd,输出观察一下
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd;//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success\r\n");printf("fd=%d\r\n",fd);}//关闭文件,收回文件描述符int ret = close(fd);if(ret == 0){printf("close success\r\n");}return 0;
}
write
函数功能:
根据文件描述符写入所需内容到文件内
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
函数参数:
int fd:文件描述符->open()的返回值
const void *buf:需要写入内容
size_t count:写入的数据的字节数
函数返回值:
类型:int
成功 返回写入内容的大小
失败 返回-1
示例:利用一下这个write函数,将对应的字符串写入到1.txt里面
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd;char str[]="hello world";//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success\r\n");printf("fd=%d\r\n",fd);}//写入内容int res =write(fd, str, sizeof(str));printf("res=%d\r\n",res);//关闭文件,收回文件描述符int ret = close(fd);if(ret == 0){printf("close success\r\n");}return 0;
}
read
函数功能:
根据文件描述符读出文件内的内容
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
函数参数:
int fd:文件描述符->open()的返回值
void *buf:存储内容的地址
size_t count:读取到数据的字节数
函数返回值:
类型:int
成功 >0 读取内容的大小
=0 达到文件的末尾
失败 返回-1
示例1:利用一下这个read函数,将1.txt里面的内容读取出来
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd;char str[15]={0};//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success\r\n");printf("fd=%d\r\n",fd);}//读取文件int res = read(fd, str, sizeof(str));printf("res=%d\r\n",res);printf("str=%s\r\n",str);//关闭文件,收回文件描述符int ret = close(fd);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2: fd=1的情况:利用一下read函数,读取文件里面的内容,将对应的bufF里面内容,write写到终端上(屏幕)
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd;char str[15]={0};//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success\r\n");printf("fd=%d\r\n",fd);}//读取文件int res = read(fd, str, sizeof(str));printf("res=%d\r\n",res);printf("str=%s\r\n",str);//显示到屏幕上write(1, str, sizeof(str));//换行write(1, "\n",1);//关闭文件,收回文件描述符int ret = close(fd);if(ret == 0){printf("close success\r\n");}return 0;
}
示例3:fd=0的情况:利用一下fd=0(键盘),可以写对应的字符,输出到屏幕上fd=1
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{char str[15]={0};//从键盘获取数据并且存储int res = read(0, str, sizeof(str));printf("res=%d\r\n",res);printf("str=%s",str);//显示到屏幕上write(1, str, sizeof(str));return 0;
}
lseek
函数功能:
调节读写的位置
头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
off_t lseek(int fd, off_t offset, int whence);
函数参数:
int fd:文件描述符->open()的返回值
off_t offset:偏移量,表示要移动的距离,单位是字节。这个值可以是正数(表示向后移动),也可以 是负数(表示向前移动)。
int whence:参考位置,用于确定offset的起始点。
SEEK_SET (文件开头) SEEK_CUR(当前读写位置) SEEK_END(文件末尾)
函数返回值:
类型:long long
成功 返回新的读写位置相对于文件头部起始位置的偏移量
失败 返回-1
示例:利用一些lseek函数,调节读写位置,观察对应的现象
现象:
SEEK_SET的情况:
SEEK_END的情况:
代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{char buff[15];int fd,val,ret;long res;//调用对应的open函数fd = open(argv[1], O_RDWR);if(fd > 0)//输出对应的文件描述符printf("fd=%d\r\n",fd);//利用read函数,将对应文件里面的字符串,获取出来read(fd,buff,sizeof(buff));printf("buff_r=%s\r\n",buff);//调节对应的读写位置res = lseek(fd, 0, SEEK_END);//下次读取的是末尾字符后面的空白内容printf("res=%ld\r\n",res);//清空数组-->为了下次接收memset(buff,0,sizeof(buff));//利用read函数,将对应文件里面的字符串,获取出来val = read(fd,buff,sizeof(buff));printf("val=%d\r\n",val);printf("buff_r=%s\r\n",buff);//关闭对应的fdret = close(fd);if(ret == 0)printf("close success\r\n");return 0;
}
4、综合示例
综合实例1:改写cat命令 最终实现的时候:./a.out 1.txt
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{int fd,res;char str[2]={0};//打开文件并且获取文件描述符,以便对文件操作fd = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd > 0){printf("open success\r\n");printf("fd=%d\r\n",fd);}//读一个数据并且显示到屏幕上while(1){//读取一字节数据res = read(fd,str,1);if(res == 0){break;}//写一字节数据到屏幕write(1,str,sizeof(str));//清空数组,以便下一次接收memset(str,0,sizeof(str));}write(1,"\n",sizeof(str));//关闭文件,收回文件描述符int ret = close(fd);if(ret == 0){printf("close success\r\n");}return 0;
}
综合实例2:改写cp命令 最终实现的时候:./a.out 1.txt 2.txt
现象:
代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{int fd1,fd2,res;char str[2]={0};//打开文件并且获取文件描述符,以便对文件操作fd1 = open(argv[1], O_RDWR | O_CREAT, 0666);//输出对应的文件描述符if(fd1 > 0){printf("open success\r\n");printf("fd1=%d\r\n",fd1);}//调用对应的open函数fd2 = open(argv[2], O_RDWR | O_CREAT,0666);//输出对应的文件描述符if(fd2 > 0){printf("open success\r\n");printf("fd2=%d\r\n",fd2);}//读一个数据并且写入到另一个文件while(1){//读取一字节数据res = read(fd1,str,1);if(res == 0){break;}//写一字节数据到另一个文件write(fd2,str,sizeof(str));//清空数组,以便下一次接收memset(str,0,sizeof(str));}//关闭文件,收回文件描述符close(fd1);close(fd2);return 0;
}
四、标准IO
1、特性
①文件ID
用标准IO的方式读写文件的前提是需要获取到linux系统分配一个文件流指针。
那什么是文件流指针呢?
文件流指针是一个指向文件数据结构的指针,该数据结构通常包含了文件的属性、状态以及当前读写位置等信息。
关于文件流指针又可以分为两类:
第一类:标准IO特殊类型—特殊情况
当一个程序开始运行时,它一般会有3个已经打开的文件流指针,就是
第一个:标准输入 文件流指针:stdin ---键盘
第二个:标准输出 文件流指针:stdout ---屏幕
第三个:标准错误 文件流指针:stderr ---错误
第二类:标准IO的一般特性
也可以称为非特殊类型,主动调用fopen()分别打开不同文件来获得对应的文件流指针--fp,文件流指针和文件是绑定在一起,并且具有唯一性,持续到调用fclose(fp),相应的程序运行就结束了。
②缓冲区
与文件的IO的主要区别,就体现在缓冲区。---只有标准IO具有缓冲区
对于大多数的标准IO函数,它的读写都是带有缓冲区,因此标准IO的读写函数需要满足一定的条件才能正常执行。
根据缓冲区的条件给标准IO分为两类:
第一类:行缓存函数---(需要满足的条件)
第一个:程序顺序执行结束的时候。--实际情况操作系统里面代码一般是带有死循环的while(1)
第二个:文件被关闭的时候---调用close函数
第三个:行缓存区被填满的时候(超过IK)。
第四个:读写函数里面包含”/n”
第二类:全缓存函数---(需要满足相应的条件)
第一个:程序顺序结束
第二个:文件被关闭的时候
第三个:全缓存区被填满的时候(超过4K)
缓存条件未满足的具体现象:
现象1:
卡在循环内,无法结束程序,故打印不了。
现象2:
卡在循环内,无法结束程序,故打印不了;文件IO无缓冲,则可以完成打印
2、操作流程
用标准IO操作流程和文件IO操作流程大致是一样的--->标准IO可以利用C库间接调用对应的文件IO
标准IO的操作过程:
打开文件->操作文件->收回文件
首先,先用fopen函数操作文件,根据实现的功能对文件进行操作fwrite()、fread(),flseek(),对文件进行操作,完成需要使用fclose(fp)。
关键点:对文件进行读写操作
3、相关函数
①与文件IO对应的标准IO
fopen
函数功能:
用于打开一个文件,并返回一个指向该文件的指针
头文件:
#include <stdio.h>
函数原型:
FILE *fopen(const char *path, const char *mode);
函数参数:
const char *path:指定了要打开文件的名称(包括路径,如果文件不在当前工作目录中)。
const char *mode:指定了文件的打开模式
mode 参数的不同取值决定了文件的打开方式和行为,常见的模式包括:
"r":以只读方式打开文件。文件必须存在。
"w":以只写方式打开文件。如果文件存在,长度被截断为0,即其内容会消失。如果文件不存在,则创建新文件。
"a":以追加方式打开文件。如果文件存在,写入的数据会被添加到文件末尾。如果文件不存在,则创建新文件。
"r+":以读/写方式打开文件。该文件必须存在。
"w+":创建一个用于读/写的空文件。如果文件已存在,则其内容会被删除。
"a+":以读/写方式打开文件,该文件用于追加。如果文件不存在,则创建新文件。
函数返回值:
成功 返回文件的指针->fp
失败 返回NULL
示例:利用一下对应的fopen函数,获取一下对应的文件流指针,输出观察一下
现象:
代码:
#include<stdio.h>
int main(int argc,char *argv[])
{FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp=fopen(argv[1], "w+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}return 0;
}
fclose
函数功能:
用于关闭一个已经打开的文件,并且释放文件流指针
头文件:
#include <stdio.h>
函数原型:
int fclose(FILE *stream);
函数参数:
FILE *stream:文件流指针->fp
函数返回值:
成功 返回0
失败 返回-1
示例:利用close函数,关闭对应的文件fd,输出观察一下
现象:
代码:
#include <stdio.h>
int main(int argc,char *argv[])
{FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen(argv[1], "w+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//关闭对应的fpint ret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
fwrite
函数功能:
根据文件流指针,将一块数据写入到文件中
头文件:
#include <stdio.h>
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:
void *ptr:指向要写入数据的内存块的指针(需要写入的内容)
size_t size:每个数据块(或元素)的大小,以字节为单位
size_t nmemb:要写入的数据块(或元素)的数量(包括'\0')
FILE *stream:文件流指针->fopen()的返回值
函数返回值:
类型:int
成功 写入的数据块(或元素)的数量
失败 返回-1
示例:利用一下这个fwrite函数,将对应的字符串写入到1.txt里面
现象:
代码:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{char buff[]="hello world";FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen(argv[1], "w+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//将内容写入文件内int val = fwrite(buff, sizeof(char), sizeof(buff),fp);printf("val=%d\r\n",val);//关闭对应的fpint ret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
fread
函数功能:
根据文件流指针,读出文件内的内容
头文件:
#include <stdio.h>
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:
void *ptr:指向要读取数据的内存块的指针(需要读取的内容)
size_t size:要读取的每个数据项的大小
size_t nmemb:要读取的数据项的数量(包括'\0')
FILE *stream:文件流指针->fopen()的返回值
函数返回值:
类型:int
成功 >0读取的数据项的数量
=0 达到文件的末尾
失败 返回-1
示例:利用一下这个fread函数,将1.txt里面的内容读取出来
现象:
代码:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{char buff[15]={0};FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen(argv[1], "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//将文件中的内容读取出来int val = fread(buff, sizeof(char), sizeof(buff),fp);printf("val=%d\r\n",val);printf("buff=%s\r\n",buff);//关闭对应的fpint ret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
fseek
函数功能:
调节读写的位置
头文件:
#include <stdio.h>
函数原型:
int fseek(FILE *stream, long offset, int whence);
函数参数:
FILE *stream:文件流指针->fopen()的返回值
long offset:偏移量,表示要移动的距离,单位是字节。这个值可以是正数(表示向后移动),也可以 是负数(表示向前移动)。
int whence:参考位置,用于确定offset的起始点。
SEEK_SET (文件开头) SEEK_CUR(当前读写位置) SEEK_END(文件末尾)
函数返回值:
类型:long
成功 返回新的读写位置相对于文件头部起始位置的偏移量
失败 返回-1
示例:利用fseek函数,调节读写位置,观察对应的现象
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={0};FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//将文件中的内容读取出来int val = fread(buff, sizeof(char), sizeof(buff),fp);printf("val=%d\r\n",val);printf("buff=%s",buff);//调节对应的读写位置long res = fseek(fp, 0, SEEK_END);//下次读取的是末尾字符后面的空白内容printf("res=%ld\r\n",res);//清空数组-->为了下次接收memset(buff,0,sizeof(buff));//利用fread函数,将对应文件里面的字符串,获取出来val = fread(buff, sizeof(char), sizeof(buff),fp);printf("val=%d\r\n",val);printf("buff_r=%s\r\n",buff);//关闭对应的fpint ret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
②标准IO的其他函数
fprintf
函数功能:
用于向指定的文件流或标准输出(如屏幕)写入格式化的数据.
头文件:
#include <stdio.h>
函数原型:
int fprintf(FILE *stream, const char *format, ...);
函数参数:
FILE *stream:这是一个指向FILE对象的指针,表示你想要写入数据的文件流。如果你想要将数据输出到标准输出(屏幕),可以使用stdout,它是stdio.h头文件中预定义的一个指向标准输出流的指针。
const char *format:这是一个格式化字符串,指定了后续参数应该如何被格式化和写入。格式化字符串中可以包含普通的字符和格式说明符。格式说明符以百分号%开始,后面跟着一个或多个字符,用于指定要写入的数据的类型和格式。
...:这是一个可变参数列表,表示你可以提供任意数量和类型的参数,这些参数将被格式化字符串中的格式说明符所引用。
函数返回值:
成功 返回输出的字符个数。
失败 返回-1
字符串的拼接 --- 例如:调用一个文件路径--/home/hu/tupian/1.bmp
示例1:调用一下fprintf函数,写入数据到文件内,观察一下对应的现象
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[]={"1.bmp"};FILE *fp;int ret;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fprintf函数,,写入数据到文件内ret = fprintf(fp,"/home/hu/hello/%s\r\n",buff);printf("ret:%d\r\n",ret);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:调用一下fprintf函数,将数据显示到屏幕,观察一下对应的现象—特殊形式stdout
现象:
代码:
#include <stdio.h>int main(int argc,char *argv[])
{int ret;char name[]="1.bmp";//直接调用对应的fprintfret=fprintf(stdout,"/home/hu/hello/%s\r\n",name);//输出观察一下printf("ret=%d\r\n",ret);return 0;
}
fscanf
函数功能:
用于从指定的文件流或标准输入(如键盘)读取格式化的数据
头文件:
#include <stdio.h>
函数原型:
int fscanf(FILE *stream, const char *format, ...);
函数参数:
FILE *stream:这是一个指向 FILE 对象的指针,表示你想要从中读取数据的文件流。
const char *format:这是一个格式化字符串,指定了如何从文件流中读取和解释数据。如果你想从键盘获取数据,可以使用stdin.
...:这是一个可变参数列表,表示你可以提供任意数量和类型的变量,这些变量将用于存储从文件流中读取的数据。
函数返回值:
成功 返回输入项的个数
失败 返回-1
注意:通过fscanf()函数获取字符串的时候,空格将被视为分割符号。
示例1:调用一下fscan函数,从文件内读取数据,观察一下对应的现象
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={0};int ret;FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fscanf函数,从文件内读取数据出来ret = fscanf(fp,"%s",buff);if(ret > 0){printf("ret=%d\r\n",ret);printf("buff:%s\r\n",buff);}//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:调用一下fscanf函数,从键盘上获取数据,并且显示到屏幕—特殊形式stdin
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={0};int ret;//调用fscanf函数,从文件内读取数据出来ret = fscanf(stdin,"%s",buff);if(ret > 0){printf("ret=%d\r\n",ret);printf("buff:%s\r\n",buff);}//显示到屏幕上fprintf(stdout,"%s\r\n",buff);return 0;
}
示例3:实现通过fscanf()函数实现两次输出—利用一些特殊情况,如果字符串中间有空格就会停止。
现象:
代码:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{FILE *fp;int ret;char str1[15];char str2[15];//调用对应的fopen函数fp = fopen("./1.txt", "r+");if(fp != NULL){printf("open success\r\n");//输出对应的文件流指针printf("fp=%p\r\n",fp);}//读取数据 fscanf(fp,"%s",str1);printf("str1:%s\r\n",str1);fscanf(fp,"%s",str2);printf("str2:%s\r\n",str2);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
fgetc
函数功能:
用于从指定的文件流中读取一个字符
头文件:
#include <stdio.h>
函数原型:
int fgetc(FILE *stream);
函数参数:
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
成功 返回读取到的字符(作为无符号字符强制转换为 int 类型)
失败 返回-1
示例1:利用fgetc函数,从文件中获取对应的字符---非特殊形式
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={0};int ret;FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fgetc函数,从文件内读取数据出来char ch=fgetc(fp);printf("ch:%c\r\n",ch);int c=fgetc(fp);printf("c:%d\r\n",c);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:利用fgetc函数,从键盘中获取对应的字符---特殊形式stdin
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{//调用fgetc函数,从键盘中获取对应的字符char ch=fgetc(stdin);printf("ch:%c\r\n",ch);int c=fgetc(stdin);printf("c:%d\r\n",c);return 0;
}
fputc
函数功能:
用于将一个字符写入到指定的文件流中
头文件:
#include <stdio.h>
函数原型:
int fputc(int char, FILE *stream);
函数参数:
int char:这是要写入文件的字符。尽管这个参数是 int 类型,但函数只会写入该整数值对应的低8位(即一个字节)作为字符。
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
成功 返回写入的字符
失败 返回-1
示例1:利用一下fputc函数,将对应的字符,写入到对应的文件里面---非特殊形式
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{int ret;FILE *fp;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fputc函数,往文件内写入数据char ch=fputc('A',fp);printf("ch:%c\r\n",ch);int c=fputc('A',fp);printf("c:%d\r\n",c);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:利用一下fputc函数,将对应的字符,显示到屏幕---特殊形式stdout
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{//调用fputc函数,将数据显示到屏幕char ch=fputc('A',stdout);return 0;
}
fgets
函数功能:
用于从指定的文件流中读取一行数据
头文件:
#include <stdio.h>
函数原型:
char *fgets(char *str, int n, FILE *stream);
函数参数:
char * str:这是一个字符数组(或字符指针指向的内存区域),用于存储从文件流中读取的数据。
int n:这是一个整数,指定了 fgets() 函数最多可以从文件流中读取的字符数(包括最后的空字符 \0)。实际上,fgets() 会在读取到 n-1 个字符或遇到换行符 \n(也会被存储在 str 中)时停止读取,并在字符串末尾添加一个空字符 \0,形成一个完整的字符串。
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
成功 对应字符串的首地址
失败 NULL
示例1:利用fgets函数,从文件中获取对应的字符串---非特殊形式
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{int ret;FILE *fp;char buff[15]={0};char *p;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fgets函数,从文件内读取数据p = fgets(buff,sizeof(buff),fp);if(p != NULL)printf("buff:%s",buff);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:利用fgets函数,从键盘中获取对应的字符---特殊形式stdin
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={0};char *p;//调用fgets函数,从屏幕获取数据p = fgets(buff,sizeof(buff),stdin);if(p != NULL)printf("buff:%s",buff);return 0;
}
fputs
函数功能:
用于将一个字符串写入到指定的文件流中
头文件:
#include <stdio.h>
函数原型:
int fputs(const char *str, FILE *stream);
函数参数:
const char *str:这是一个指向字符数组(或字符串)的指针,包含了要写入文件的数据。字符串不需要以空字符 \0 结尾(尽管在实际使用中,字符串通常都是以 \0 结尾的),但 fputs() 会一直写入直到遇到第一个 \0 或写入了 str 指向的字符数组中的所有字符。
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
成功 返回非负数
失败 返回-1
示例1:利用一下fputs函数,将对应的字符串,写入到对应的文件里面---非特殊形式
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{int ret;FILE *fp;char buff[15]={"hello_world"};int p;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//调用fputs函数,往文件内写入数据p = fputs(buff,fp);if(p>0)printf("write success\r\n");//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
示例2:利用一下fputs函数,将对应的字符串,显示到屏幕---特殊形式stdout
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{char buff[15]={"hello_world"};int p;//调用fputs函数,将字符串显示到屏幕p = fputs(buff,stdout);return 0;
}
feof
函数功能:
用于检测文件流是否已经到达文件末尾
头文件:
#include <stdio.h>
函数原型:
int feof(FILE *stream);
函数参数:
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
如果读写位置达到文件的末尾,则返回非0
如果读写位置没有达到文件的末尾,则会返回0
示例:利用一下feof函数,判断对应的文件是否到达文件的末尾。
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{int ret;FILE *fp;char buff[2]={0};int p;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//判断对应的文件是否到达文件的末尾while(feof(fp)==0){//每次读取一字节数据fread(buff,sizeof(char),1,fp);//显示到屏幕fwrite(buff,sizeof(char),1,stdout);//清空数组memset(buff,0,sizeof(buff));}//关闭对应的fpret =fclose(fp);if(ret == 0){printf("\nclose success\r\n");}return 0;
}
fflush
函数功能:
用于清空输出缓冲区或更新输入缓冲区(对于某些特定类型的输入流)
头文件:
#include <stdio.h>
函数原型:
int fflush(FILE *stream);
函数参数:
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
成功 返回0
失败 返回-1
示例:验证一下fflush这个函数,观察对应的现象
现象:
代码:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{int ret; printf("test funtion");//更新输入缓冲区fflush(stdout);if(ret == 0){printf("\nfflush success\r\n");}while(1);return 0;
}
ftell
函数功能:
用于获取文件流中当前位置的偏移量
头文件:
#include <stdio.h>
函数原型:
long ftell(FILE *stream);
函数参数:
FILE *stream:这是一个指向 FILE 类型的指针,代表要写入的文件流
函数返回值:
类型:long
成功 从文件开头到当前位置的偏移量(以字节为单位)。
失败 返回-1
示例:计算一个文件里面的字节数
现象:
代码:
#include <stdio.h>
#include <string.h>int main(int argc,char *argv[])
{int ret;FILE *fp;char buff[2]={0};long res;//打开文件并且获取文件流指针,以便对文件操作fp = fopen("1.txt", "r+");//输出对应的文件流指针if(fp != NULL){printf("open success\r\n");printf("fp=%p\r\n",fp);}//判断对应的文件是否到达文件的末尾while(feof(fp)==0){//每次读取一字节数据fread(buff,sizeof(char),1,fp);//显示到屏幕fwrite(buff,sizeof(char),1,stdout);//清空数组memset(buff,0,sizeof(buff));}//确保到达文件的末尾fseek(fp, 0, SEEK_END);//获取当前位置的偏移量res=ftell(fp);printf("\nres=%ld\r\n",res);//关闭对应的fpret =fclose(fp);if(ret == 0){printf("close success\r\n");}return 0;
}
总结
系统调用接口调用API函数的过程
用户进程通过系统调用进入到linux内核层,在进入内核层后,不同的系统调用会找到各自对应的内核函数,处理完后再次通过系统调用返回到用户空间。
就比如,在内核中实现系统调用write()的过程。首先,用户进程调用write()这个API函数,write函数会进行一些检查(如文件描述符的有效性),然后将控制权转移到内核态,通过系统调用接口来完成实际的写入操作,因此程序运行空间从用户空间进入内核空间;在进入内核层后,系统调用会找到内核函数sys_write,sys_write函数会验证参数,检查文件描述符的有效性,并根据文件描述符的类型(如普通文件、管道、套接字等)执行相应的写入操作;最后,内核将写入操作的结果(成功写入的字节数或错误码)通过系统调用接口返回给用户态的write函数,最终返回给用户程序。