I/O操作是实现一切的基础
- 标准io – stdio(库)
- 系统调用io – sysio(文件io)
优先使用标准io=
stdio:
- fopen man fopen
- fclose
- fgetc
- fputc
- fgets
- fputs
- fread
- fwrite
- fseek
- ftell
- rewind
fflush
// begin / current / end of file
// r r+ 是文件必须存在的,其他都会被创建
perror(const char *); // 将errno显示成出错信息,自动关联了errno变量00,比如perror("fopen");
// 输出错误流 strerror解析错误号的内容
fprintf(stderr, "fopen() %s\n", strerror(errno));
FILE *fopen(const char *pathname, const char *mode);
File* 这个指针指向哪里?
栈? 静态区? 堆?
- 看fopen函数可以理解,如果返回在栈里面,需要先声明临时变量,最后返回&file,但是这是临时变量,返回临时变量的地址是不对的,它会在返回之前被释放
- 静态区:如果在静态区,那么这个file变量仅仅会被声明一次,那么每打开一个文件,前一个打开的文件存储就会被冲掉,无法实现打开多个文件
- 因此这个file指针应该是指向堆的,这就是为什么fopen之后会对应一个fclose的原因,fclose里面做的就是释放堆的空间
总结: 小技巧识别,一般情况下有一对操作 (open_close)的,是存放在堆上的,其他一般是在静态区。
默认最多可以打开多少个文件
一个进程使用的时候,默认会打开三个流(标准输入, 输出,出错)
ulimit -a 命令中open files可更改
ll
新文件权限
0666 & -umask
比如此时umask是0002 那么使用0666 & -0002
&1就是值不变,&0就是那一位被消掉(设置为0)
结果就是2->010->取反101 即0处被消掉,即666->666-2=664
umask值越大,被消掉的权限越多,新文件权限越低
- fgetc
- fputc
// 复制文件操作
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
int ch;
if (argc < 3)
{
fprintf(stderr, "useage: %s, <src_file> <dst_file>\n",argv[0]);
exit(0);
}
FILE* fps = fopen(argv[1], "r");
if (fps == NULL)
{
perror("fopen");
exit(1);
}
FILE* fpd = fopen(argv[2], "w");
if (fpd == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
while (1)
{
ch = fgetc(fps);
if (ch == EOF)
{
break;
}
fputc(ch, fpd);
}
fclose(fpd);
fclose(fps);
return 0;
}
fgets(*, size, stream)
// size -1 '\n' 会停止
fputs
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
int ch;
if (argc < 3)
{
fprintf(stderr, "useage: %s, <src_file> <dst_file>\n",argv[0]);
exit(0);
}
FILE* fps = fopen(argv[1], "r");
if (fps == NULL)
{
perror("fopen");
exit(1);
}
FILE* fpd = fopen(argv[2], "w");
if (fpd == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
char buf[1024];
while (fgets(buf, 1024, fps) != NULL)
{
fputs(buf, fpd);
}
fclose(fpd);
fclose(fps);
return 0;
}
第三个变量是有多少个成员,第二个变量是每个成员的大小,一般设置为1,当作字符去做
返会成功个数 fread(void* ptr, size_t size, size_t mnum, FILE* stream); // 从stream读到ptr
fwrite
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
int ch;
if (argc < 3)
{
fprintf(stderr, "useage: %s, <src_file> <dst_file>\n",argv[0]);
exit(0);
}
FILE* fps = fopen(argv[1], "r");
if (fps == NULL)
{
perror("fopen");
exit(1);
}
FILE* fpd = fopen(argv[2], "w");
if (fpd == NULL)
{
fclose(fps);
perror("fopen");
exit(1);
}
char buf[1024];
int n;
while ((n = fread(buf, 1 , 1024, fps) != NULL) > 0) // 读到多少写多少
{
fwrite(buf, 1, n fpd);
}
fclose(fpd);
fclose(fps);
return 0;
}
fprintf(输出的流, “”, 格式的变量);
fprintf(stderr, "usage:%s", args[0]);
sprintf(char* str_tar, const char *format…);
char buf[100];
sprintf(buf, "格式化输出". 格式化变量); // 串的大小不清楚,缓冲区大小不定 用snprintf弥补
puts(buf);
sprintf(buf, n, "", 格式化变量); // 可以容纳 n-1个字符
fscanf(流, “格式化”);
// 操作文件位置指针 文件在关闭之前,文件指针是随着操作变化的,只有关闭之后再打开,才会重新定位,
ftell(FILE*); // 定位文件中的一个位置,但是文件大小不超过2G
ftello(FILE*); // 定位文件中的一个位置,但是编译的时候需要添加编译的宏,#define _FILE_OFFSET_BITS 64
fseek(FILE* , off, 宏); // 哪个流 , 偏移多少, 相对头还是尾偏移
// 目前没有一个函数可以任意取多少个字符
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc < 3)
{
sprintf(stderr, "usage:%s <src> <dst>", argv[1]);
exit(1);
}
FILE * ptr = NULL;
int count = 0;
ptr = fopen(argv[1], "r");
if (ptr == NULL)
{
perror("fopen");
exit(1);
}
fseek(ptr, 0, SEEK_END); // 定个位到末尾
fprintf(stdout, "%d\n", ftell(ptr)); // 输出当前指针位置
fclose(ptr);
return 0;
}
rewind(流); 使用之后,会将指针设置到文件首
fflush();
什么都不会打印,默认刷新缓冲是换行符,
int main()
{
int i;
printf("before");
while (1);
printf("after");
return 0;
}
int main()
{
int i;
printf("before\n"); // 会输出
// 或者使用printf("before"); fflush(stdout);
while (1); // 后续不再输出
printf("after");
return 0;
}
缓冲区存在的作用,合并系统调用
- 行缓冲:换行时刷新/ 满了刷新/强制fflush刷新----stdout–因为是终端设备
- 全缓冲:满了刷新,/ 强制刷新—默认只要不是终端设备就是
- 无缓冲:比如stderr,需要立即输出内容
getline();
从stream中读取一行
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
if (argc < 2)
{
fprintf(stderr, "usage\n");
exit(1);
}
FILE* fp = fopen(argv[1], "r");
if (fp == NULL)
{
perror("fopen");
exit(1);
}
char* linbuf;
size_t linesize;
// 段错误隐患, 初始化
linbuf = NULL;
linesize = 0;
while (1)
{
// getline本质是malloc,后续增大会realloc,但是没有出现free操作,因此这里存在内存泄露风险, 一行多大都会malloc读出来
// 但是这里是可控的,只有linesize这么大的大小
if (getline(&linbuf, &linesize, fp) < 0)
{
break;
}
printf("%d\n", strlen(linbuf));
}
fclose(fp);
return 0;
}
临时文件
- 如何不冲突的创建临时文件
- 及时销毁
tmpnam 没有办法安全的创建
tmpfile