还可以参考这些内容
<<UNIX环境高级编程第三版>>
Linux系统编程-文件IO - 骆三疯 - 博客园 (cnblogs.com)
Linux 安装中文 man 手册_linux man中文手册-CSDN博客
程序各个段text,data,bss,stack,heap - James110 - 博客园 (cnblogs.com)
C/C++程序存储区_c++ 哪些会存储在静态存储区-CSDN博客
C语言fgetc和fputc函数用法详解(以字符形式读写文件)_c语言库函数fgetc(fp)的功能是( ) 从文件fp中读取第一个字符 向屏幕上输出一个字-CSDN博客
C标准库的文件IO函数----------
- fopen、fclose、fseek、fgets、fputs、fread、fwrite......
- 在命令行,通过 man fopen...... 等可以查看系统定义的对应的标库函数
fopen打开文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
FILE *fp;
fp = fopen("tem","r");
if(fp == NULL)
{
fprintf(stderr,"fopen() failed err = %d\n",errno);
exit(1);
}
puts("ok");
exit(0);
}
找不到文件的时候,返回了err = 2;
打开errno-bash.h可以看到刚好对应
这样查看报错显然太慢了
方法1使用函数perror
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
FILE *fp;
fp = fopen("tem","r");
if(fp == NULL)
{
//fprintf(stderr,"fopen() failed err = %d\n",errno);
perror("fopen()"); //自动关联全局变量errno
exit(1);
}
puts("ok");
exit(0);
}
结果
方法2
strerror()
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
FILE *fp;
fp = fopen("tem","r");
if(fp == NULL)
{
//fprintf(stderr,"fopen() failed err = %d\n",errno);
//perror("fopen()"); //自动关联全局宏定义errno
fprintf(stderr,"fopen:%s\n",strerror(errno));
exit(1);
}
puts("ok");
exit(0);
}
栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
堆区(heap) : 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
全局区(静态存储区)(static): 全局变量和静态变量的存储是放在一块的,a .初始化的全局变量和静态变量在一块区域(.data段), b .未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss段)。程序结束后有系统释放
文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放
代码区—存放函数体的二进制代码。
我们将文字常量区和代码区视为一个部分,即代码区
其中fopen返回的指针是放在堆上的。、
有打开就有关闭
fclose关闭文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
FILE *fp;
fp = fopen("teiiiim","r");
if(fp == NULL)
{
//fprintf(stderr,"fopen() failed err = %d\n",errno);
perror("fopen()"); //自动关联全局宏定义errno
//fprintf(stderr,"fopen:%s\n",strerror(errno));
exit(1);
}
puts("ok");
fclose(fp);
exit(0);
}
ps: 解决vscode报错: 命令行错误: 指定的语言模式不兼容, c/c++:1027-CSDN博客
这里改个报错,命令行错误: 指定的语言模式不兼容, c/c++:1027
循环打开文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
int i = 0;
FILE *fp;
while(1)
{
fp = fopen("tem","w");
if(fp == NULL)
{
perror("fopen()"); //自动关联全局宏定义errno
break;
//exit(1);
}
else
{
i++;
}
}
printf("%d\n",i);
puts("ok");
exit(0);
}
结果
输入ulimit -a 查看最多可以打开的文件限制,当然这里是可以改的,这里可以看到为1048576
但是我的数值却是1048559 少的17去哪里了?这里和操作环境有关,这里还打开了其他的文件流
按字符读写 fgetc、fputc
int fgetc (FILE *fp);
1,fp为文件指针 ,fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF。
2,在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。
注意:这个文件内部的位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是隐藏的。
EOF 不绝对是 -1,也可以是其他负数,这要看编译器的实现。
fputc
是 file output char 的所以,意思是向指定的文件中写入一个字符
int fputc ( int ch, FILE *fp );
把字符U写入*fp中
fputc('a', fp);
下面应用实现一个简单的mycopy函数,实现文件的复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc,char **argv)
{
if(argc < 3) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
exit(1);
}
FILE *fps,*fpd;
int ch;
fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写
if(fps == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fpd);
perror("fopen");
exit(1);
}
while(1)
{
ch = fgetc(fps);
if(ch == EOF) //读到文件尾的话
break;
fputc(ch,fpd); //把内容写到fpd中
}
fclose(fpd);
fclose(fps);
exit(0);
}
执行结果
可以看到生成了一个和list2.h一样的7777.h文件
文本字符数量计数小练习
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc,char **argv)
{
FILE *fp = NULL;
int count = 0;
if(argc < 2) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file>\n>",argv[0]);
exit(1);
}
fp = fopen(argv[1],"r"); //打开文件 argv[1] 只写
if(fp == NULL)
{
fclose(fp);
perror("fopen");
exit(1);
}
while(fgetc(fp) != EOF)
{
count++;
}
printf("%d\n",count);
exit(0);
}
fgets和fputs
char *gets(char *s);
char *fgets(char *s, int size ,FILE *stream);
fgets 读到 size -1 或者\n 会结束读取
#include <stdio.h>
#include <stdlib.h>
#define SIZE 5
char buf[SIZE];
fgets(buf,SIZE,stream);
读取 abcdef
SIZE = 5时, buf中存入的是 a b c d \0
读取 ab
SIZE = 5时, buf中是 a b \n \0 后面不管
读取 abcd SIZE = 5时 读入2次才能读完
1 -> a,b,c,d \0
2 -> \n \0
fputs用法
fputs(buf,stream);
使用fgets和fputs改写上面的mycopy函数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int main(int argc,char **argv)
{
if(argc < 3) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
exit(1);
}
FILE *fps,*fpd;
// int ch;
char buf[SIZE];
fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写
if(fps == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fpd);
perror("fopen");
exit(1);
}
while(fgets(buf,SIZE,fps) != NULL)
{
fputs(buf,fpd);
// ch = fgetc(fps);
// if(ch == EOF) //读到文件尾的话
// break;
// fputc(ch,fpd); //把内容写到fpd中
}
fclose(fpd);
fclose(fps);
exit(0);
}
结束同样生成了复制文件
fread和fwrite
这两个适合操作数据量工整数据
当 情况1->数据量足够
当 情况2- >只有5个字节时候
------------------
fread(buf,1,10,fp); //读取10个对象每次一个字节
情况1-> 10 -> 10字节
情况2-> 这时候读了5个字节,剩下5读不到
fread(buf,10,1,fp); //读取1个对象每次10字节
情况1-> 1 -> 10字节
情况2 -> 数据不够,但是返回读取到的数目
这里同样用它改一个copy代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int n = 1;
int main(int argc,char **argv)
{
if(argc < 3) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
exit(1);
}
FILE *fps,*fpd;
// int ch;
char buf[8];
fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写
if(fps == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fpd);
perror("fopen");
exit(1);
}
while(n > 0)
{
n = fread(buf,1,SIZE,fps);
fwrite(buf,1,n,fpd);
// ch = fgetc(fps);
// if(ch == EOF) //读到文件尾的话
// break;
// fputc(ch,fpd); //把内容写到fpd中
}
fclose(fpd);
fclose(fps);
exit(0);
}
结果
还有很多IO操作的函数,详情要看man手册
print&scan...
输入man 3 printf
fseeko & ftello
fseek &ftell
定位一个流
fseek(指定流,偏移量,偏移相对位置(从当前位置向前或者向后走))
伪码
fp = fopen();
fputc(fp); 读取10次后,文件指针后移10
这个时候用 fseek往前把指针移动10
fseek(fp,-10,SEEK_CUR); //SEEK_CUR是从当前位置
再使用fgetc() 便可以读取刚才写入的内容
成功返回0,否则返回-1
ftell(指定流) 返回当前文件指针的位置
写一个检查文件大小的demo
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int n = 1;
int main(int argc,char **argv)
{
if(argc < 2) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file>\n>",argv[0]);
exit(1);
}
FILE *fps;
// int ch;
char buf[8];
fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写
fseek(fps,0,SEEK_END);//把文件指针移动到文件末尾
printf("%ld\n",ftell(fps)); //打印指针位置,这里就能获得字节数目,从而得知文件大小
fclose(fps);
exit(0);
}
结果可以看到是606个字节
查看文件属性后确实是606个字节
fseecko ftello 比不带o的支持文件上限自定义,fseeck ftell 可以看到offset类型时long 最多支持2G文件大小
fseeck可以用于空洞文件
空洞文件: 刚建立下载文件时就占用文件大小的空间,这种文件里面全是 \0
缓冲区
fflush();
fflush()
是一个 C 标准库函数,用于刷新指定流的缓冲区。它的作用取决于参数:
-
如果参数是一个指向输出流的指针(如
stdout
),则fflush()
将强制将输出缓冲区中的内容写入到文件中。这对于确保数据被立即写入到文件中,而不是等到缓冲区满或者程序结束时才写入,非常有用。 -
如果参数是一个指向输入流的指针(如
stdin
),fflush()
会产生未定义的行为,因为标准不允许刷新输入流的缓冲区。在输入流上调用fflush()
通常没有意义,并且会导致不可预测的结果。 -
输入NULL,刷新所有打开的流
缓冲区:
用于合并系统调用,
1,行缓冲 换行时刷新,满的时候刷,强制刷新(例如fflush();) ps (标准输出时是这样的,因为是终端设备)
2,全缓冲 满的时候刷,强制刷新 ps(默认时基本上都是全缓冲模式,除了终端设备)
3,无缓冲 直接刷新 如stderr就是无缓冲(出错了还等什么😋)
setvbuf() 更改缓冲模式 一般用不到
setvbuf()
是一个 C 标准库函数,用于设置流的缓冲方式。它允许你手动控制输入流和输出流的缓冲区。函数的原型如下:
int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
参数解释如下:
stream
:指向 FILE 结构的指针,用于指定要设置缓冲方式的流。buffer
:指向一个缓冲区的指针,用于设置流的缓冲区。如果为 NULL,则系统自动分配缓冲区。mode
:用于指定缓冲类型的整数值,可以是以下值之一:_IOFBF
:完全缓冲。数据在流的内部缓冲区中被积累,直到缓冲区满或显式调用fflush()
或setvbuf()
时才会写入文件。_IOLBF
:行缓冲。数据在缓冲区中按行积累,直到出现换行符\n
或缓冲区满时才写入文件。_IONBF
:无缓冲。数据直接写入文件,没有缓冲。
size
:缓冲区大小(以字节为单位),仅在提供自定义缓冲区时使用。如果为 0,则缓冲区大小由系统决定。
getline()
用于从文件流中读取一行文本,并将其存储到一个动态分配的缓冲区中。
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
lineptr
:一个指向字符指针的指针,用于存储读取到的文本。如果*lineptr
为 NULL,getline()
会为其分配内存;如果不为 NULL,则getline()
会尝试重用现有缓冲区,如果缓冲区不足以存储整行文本,则会重新分配足够大的内存。n
:一个指向size_t
类型变量的指针,用于指定*lineptr
指向的缓冲区大小。如果*lineptr
为 NULL,则n
的值被忽略;如果*lineptr
不为 NULL,则n
指定了*lineptr
指向的缓冲区的大小。如果读取的行文本长度超过了缓冲区大小,则getline()
会自动调整缓冲区大小。stream
:一个指向 FILE 结构的指针,表示要从中读取文本的流。
getline()
函数会返回读取到的字符数(包括换行符,不包含尾0),如果到达文件末尾或发生错误,则返回 -1。在成功读取一行后,*lineptr
指向的缓冲区中将包含该行文本(以 null 结尾),而 n
的值将被更新为缓冲区的大小。
打印文件每行的字符数
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
FILE *fp;
char *linebuf;
size_t linesize;
if(argc < 2) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s",argv[0]);
exit(1);
}
fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen");
exit(1);
}
/*!!!小心段错误!!!!*/
linebuf = NULL;
linesize = 0;
while(1)
{
if(getline(&linebuf,&linesize,fp) < 0)
{
break;
}
printf("%ld\n",strlen(linebuf));//打印字符个数
}
free(linebuf);//!!!!!!!!!!!!!!!!!!!!!!!!
fclose(fp);
}
第一行包含换行(\n)刚好20个字符
注意用完要free(),不然就内存泄漏了😒👿👿👿👿👿
临时文件
tmpnam(char *s);
为一个临时文件创一个名字 并发会有问题
FILE *tmpfile(void);
创建临时文件返回文件指针(这个文件看不到的->>匿名文件)
关闭文件或者进程终止时的时候就释放了
文件IO/系统调用IO------------
文件IO操作:open,close,read,write,lseek
文件描述符实现原理
Linux——什么是文件描述符_文件描述符 linux-CSDN博客
1.系统调用IO-文件描述符实现原理_哔哩哔哩_bilibili
open close read write lseek
实现文件复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // 包含这些头文件
#define SIZE 1024
int main(int argc,char **argv)
{
int sfd,dfd;
int len,ret;
char buf[SIZE];
if(argc < 3) //检查输入参数数量是否正确
{
fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
exit(1);
}
sfd = open(argv[1],O_RDONLY); //打开文件 argv[1] 只写
if(sfd < 0)
{
perror("open err");
close(sfd);
exit(1);
}
dfd = open(argv[2],O_WRONLY |O_CREAT|O_TRUNC,0600);
if(dfd < 0)
{
perror("open err");
close(dfd);
exit(1);
}
while(1)
{
len = read(sfd,buf,SIZE); //读sfd放入buf,大小为SIZE 返回字节大小
if(len < 0)
{
perror("read err");
break;
}
if(len == 0) break;
ret = write(dfd,buf,len); //写buf到dfd,长度len
if(ret < 0)
{
perror("write err");
break;
}
}
close(sfd);
close(dfd);
exit(0);
}
lseek 和fseek用法一样
文件IO和标准IO区别
文件IO无缓冲区,实时性强
标准IO有缓冲区,数据吞吐量大
两种IO转换
标准IO转文件IO
文件IO转标准IO
当然,极不建议两个混着用,标准IO有缓冲区,文件指针只有刷新缓冲区的时候才移动。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // 包含这些头文件
#include <errno.h>
#include <string.h>
int main(void)
{
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
printf("\n");
exit(0);
}
查看系统调用过程
strance ./out
系统IO先写入b b b,然后标准IO 调用系统IO一次性写入aaa\n
TIME
time ./out 查看各个部分执行时间
重定向
输出重定向到文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/home/qianghuizhe/c_learn2make/ttt.c"
int main(int argc,char **argv)
{
int fd;
close(1);
fd = open(FNAME,O_WRONLY |O_CREAT|O_TRUNC,0666);
if(fd < 0)
{
perror("open");
exit(1);
}
//-----------
//puts("hello");
printf("hello\n");
exit(0);
}
原子操作 . 原理与底层实现___sync_val_compare_and_swap-CSDN博客
作用:解决竞争和冲突
使用dup输出重定向
dup2 可以复制文件描述符到另外一块空间,指向同一个结构体,
dup2在 fd = 1(新的fd) dup2什么都不做
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/home/qianghuizhe/c_learn2make/ttt.c"
int main(int argc,char **argv)
{
int fd;
fd = open(FNAME,O_WRONLY |O_CREAT|O_TRUNC,0666);
if(fd < 0)
{
perror("open");
exit(1);
}
// close(1);
// dup(fd);
dup2(fd,1);
close(fd);
//-----------
puts("hello");
//printf("hello\n");
exit(0);
}
OTHER
sysnc
fsync
fdatasync
fcntl
ioctl 设备相关
/dev/fd/ 显示当前进程文件描述符信息
其他待补充内容
chomd 分为User、Group、及Other的权限,都是 R W X
4 2 1
代码段。。。
代码段。。。
代码段。。。
代码段。。。