1:读写流
fgets fputs getc putc的使用
1 #include"apue.h"
2 int main()
3 {
4 char buf[256];
5 int c;
6 while((fgets(buf,256,stdin)) != NULL)
7 {
8 if(fputs(buf,stdout) == EOF)
9 err_sys("output error");
10 }
11 if(ferror(stdin))
12 err_sys("input error");
13
14 exit(0);
15 }
1 #include"apue.h"
2 int main()
3 {
4 char buf[256];
5 int c;
6 while((c = getc(stdin)) != EOF)
7 {
8 if(putc(c,stdout) == EOF)
9 err_sys("output error");
10 }
11 if(ferror(stdin))
12 err_sys("input error");
13
14 exit(0);
15 }
fgetc与fputc这两个一定是函数,不是宏,相对于getc和putc,效率会低一些。
临时文件:
一、背景
有时程序需要存储很大量的数据,或者在几个进程间交换数据,这时您可能考虑到使用临时文件。使用临时文件要考虑几个问题:
1、保证临时文件间的文件名不互助冲突。
2、保证临时文件中内容不被其他用户或者黑客偷看、删除和修改。
在linux下有专门处理临时文件的函数,先简单接收两个函数。
二、简单实用的两个函数:mkstemp和tempfile
2.1 mkstemp函数
头文件:#include <stdlib.h>
函数原型:int mkstemp(char * template);
执行:mkstemp函数在系统中以唯一的文件名创建一个文件并打开,而且只有当前用户才能访问这个临时文件,并进行读、写操作。mkstemp函数只有一个参数,这个参数是个以“XXXXXX”结尾的非空字符串。mkstemp函数会用随机产生的字符串替换“XXXXXX”,保证了文件名的唯一性。
结果: 函数返回一个文件描述符,如果执行失败返回-1。在glibc 2.0.6 以及更早的glibc库中这个文件的访问权限是0666,glibc 2.0.7以后的库这个文件的访问权限是0600。
备注:临时文件使用完成后应及时删除,否则临时文件目录会塞满垃圾。由于mkstemp函数创建的临时文件不能自动删除,所以执行完mkstemp函数后要调用unlink函数,unlink函数删除文件的目录入口,但临时文件还可以通过文件描述符进行访问,直到最后一个打开的进程关闭文件操作符,或者程序退出后临时文件被自动彻底地删除。
三、内存流
内存流写入代码
#include "apue.h"
#define BSZ 48
int main()
{
FILE *fp;
char buf[BSZ];
memset(buf, 'a', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
if ((fp = fmemopen(buf, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
printf("Initial buffer contents: %s\n", buf);
fprintf(fp, "hello, world"); //写进缓存
printf("Before flush: %s\n\n", buf);
fflush(fp); //调用fflush、fclose、、fseek、fseeko、fsetpos会在当前位置添加null
printf("After fflush: %s\n", buf);
printf("Len of string in buf = %ld\n\n", (long)strlen(buf));
memset(buf, 'b', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
fprintf(fp, "hello, world");
fseek(fp, 0, SEEK_CUR); //保持偏移值冲洗之后的位置
printf("After fseek: %s\n", buf);
printf("Len of string in buf = %ld\n\n", (long)strlen(buf));
memset(buf, 'c', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
fprintf(fp, "hello, world"); //继续写进去
fseek(fp, 0, SEEK_SET); //偏移值设为缓冲区开始位置
printf("After fseek: %s\n", buf);
printf("Len of string in buf = %ld\n\n", (long)strlen(buf));
memset(buf, 'd', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
fprintf(fp, "hello, world"); //继续写进去
fclose(fp); //然后fclose在当前位置也就是数据尾端添加一个null
printf("After close: %s\n", buf);
printf("Len of string in buf = %ld\n", (long)strlen(buf));
return(0);
}
写入操作分析
首先是使用a字符修改缓冲区:
memset(buf, ‘a’, BSZ-2);
buf[BSZ-2] = ‘\0’;
buf[BSZ-1] = ‘X’;
此时得到的buf应该是:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0X
为了识别方便,这里每隔5个字符使用一个+隔开显示,如下所示:
aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + a0X
此时偏移值指向数据末尾,即offset = 47
然后执行:
if ((fp = fmemopen(buf, BSZ, “w+”)) == NULL)
err_sys(“fmemopen failed”);
流控制参数为w+,fmemopen函数在缓冲区开始处放置了null字节,此时的buf应该是:
null+ aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + 0X
因为首字符即为null,此时打印结果为空;
此时偏移值指向缓冲区开始null字节处,即offset = 0
然后通过fprintf函数写入数据,并使用fflush函数冲洗缓冲区:
fprintf(fp, “hello, world”); //写进缓存
fflush(fp); //调用fflush、fclose、、fseek、fseeko、fsetpos会在当前位置添加null
利用fflush函数引起缓冲区冲洗,并在当前位置设置null,此时的buf为:
hello, world + null + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaaaa + aaa0X
此时的打印结果为: “hello, world”
此时的偏移值指向null,即offset = 12
然后继续执行:
memset(buf, ‘b’, BSZ-2);
buf[BSZ-2] = ‘\0’;
buf[BSZ-1] = ‘X’;
用b字符改写缓冲区,此时的buf为:
bbbbb + bbbbb + bbbbb + bbbbb + bbbbb + bbbbb + bbbbb + bbbbb + bbbbb + b0X
此时的偏移值仍旧指向原来的位置,即为offset = 12
然后继续想缓冲中写入数据:
fprintf(fp, “hello, world”); //继续写进
fseek(fp, 0, SEEK_CUR); //保持偏移值在冲洗之后位置
利用fseek函数引起缓冲区冲洗,并在当前位置设置null,此时buf为:
bbbbb + bbbbb + bb + hello, world + null + bbbbb + bbbbb + bbbbb + bbbbb + b0X
打印结果为: bbbbbbbbbbbbhello, world
偏移值位于null,即offset = 24
然后经过继续写入之后:
memset(buf, ‘c’, BSZ-2);
buf[BSZ-2] = ‘\0’;
buf[BSZ-1] = ‘X’;
fprintf(fp, “hello, world”); //继续写进去
fseek(fp, 0, SEEK_SET); //偏移值设为缓冲区开始位置
利用fseek函数引起缓冲区冲洗,并在当前位置设置null,此时的buf为:
cccccccccccc + cccccccccccc + hello, world + null + ccccc + cccc0X
打印结果为:cccccccccccccccccccccccchello, world
此时由于fseek函数,偏移值设为了缓冲区的开始位置,即offset = 0
最后执行写入,注意此时的偏移值位于缓冲区开始:
memset(buf, ‘d’, BSZ-2);
buf[BSZ-2] = ‘\0’;
buf[BSZ-1] = ‘X’;
fprintf(fp, “hello, world”); //继续写进去
fclose(fp); //然后fclose在当前位置也就是数据尾端添加一个null
直接使用fclose函数关闭流,没有追加null,此时的buf为:
hello, world + null + ddddd + ddddd + ddddd + ddddd + ddddd + ddddd + ddd0X
打印结果为 hello, worlddddddddddddddddddddddddddddddddddd
但是前面不是说了一旦调用fclose等函数,就会自动在当前位置写一个null字节嘛, 为什么这里没有追加呢?请看下面三个测试方案
null追加策略分析
#include "apue.h"
#define BSZ 48
int main()
{
FILE *fp1, *fp2, *fp3;
char buf1[BSZ], buf2[BSZ], buf3[BSZ];
//方案一
memset(buf1, 'a', BSZ-2);
buf1[BSZ-2] = '\0';
buf1[BSZ-1] = 'X';
if ((fp1 = fmemopen(buf1, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
fprintf(fp1, "hello, world");
//调用fflush函数引起缓冲区冲洗
fflush(fp1);
printf("1.After fflush: %s\n", buf1);
printf("1.Len of string in buf = %ld\n", (long)strlen(buf1));
memset(buf1, 'b', BSZ-2);
buf1[BSZ-2] = '\0';
buf1[BSZ-1] = 'X';
//二次输入数据为"nihao",长度较短
fprintf(fp1, "nihao");
fclose(fp1);
printf("1.After close: %s\n", buf1);
printf("1.Len of string in buf = %ld\n\n", (long)strlen(buf1));
//方案二
memset(buf2, 'a', BSZ-2);
buf2[BSZ-2] = '\0';
buf2[BSZ-1] = 'X';
if ((fp2 = fmemopen(buf2, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
fprintf(fp2, "hello, world");
//调用fseek函数引起缓冲区冲洗,偏移值设为首部
fseek(fp2, 0, SEEK_SET);
printf("2.After fseek: %s\n", buf2);
printf("2.Len of string in buf = %ld\n", (long)strlen(buf2));
memset(buf2, 'b', BSZ-2);
buf2[BSZ-2] = '\0';
buf2[BSZ-1] = 'X';
//二次输入数据为"nihao",长度较短
fprintf(fp2, "nihao");
fclose(fp2);
printf("2.After close: %s\n", buf2);
printf("2.Len of string in buf = %ld\n\n", (long)strlen(buf2));
//方案三
memset(buf3, 'a', BSZ-2);
buf3[BSZ-2] = '\0';
buf3[BSZ-1] = 'X';
if ((fp3 = fmemopen(buf3, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
fprintf(fp3, "hello, world");
//调用fseek函数引起缓冲区冲洗,偏移值设为首部
fseek(fp3, 0, SEEK_SET);
printf("3.After fseek: %s\n", buf3);
printf("3.Len of string in buf = %ld\n", (long)strlen(buf3));
memset(buf2, 'b', BSZ-2);
buf2[BSZ-2] = '\0';
buf2[BSZ-1] = 'X';
//二次输入数据为"hello, world! How are you?",长度较长
fprintf(fp3, "hello, world! How are you?");
fclose(fp3);
printf("3.After close: %s\n", buf3);
printf("3.Len of string in buf = %ld\n\n", (long)strlen(buf3));
return(0);
}
1.After fflush: hello, world
1.Len of string in buf = 12
1.After close: bbbbbbbbbbbbnihao
1.Len of string in buf = 17
2.After fseek: hello, world
2.Len of string in buf = 12
2.After close: nihaobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
2.Len of string in buf = 46
3.After fseek: hello, world
3.Len of string in buf = 12
3.After close: hello, world! How are you?
3.Len of string in buf = 26
结果分析
方案一:fclose函数冲洗内存流导致需要增加流缓冲区中数据量,在当前偏移值后面继续写入数据,由12增加到17,并且调用了fclose函数,所以追加null;
方案二:fclose函数冲洗内存流时,偏移值在流缓冲区首部,此时”nihao”的长度小于原本流缓冲区中”hello, world”的长度,只会覆盖流缓冲区的前一部分,流缓冲区数据量并没有增加,仍未12,所以不追加null;
方案三:同方案二,只是再次输入的数据”hello, world! How are you?”大于原本流缓冲区中的”hello, world”的长度,所以需要增加流缓冲区中数据量,调用了fclose函数,所以追加’null`;
总结
由上述测试可以发现,追加null的策略机制是必须同时满足以下两个条件:
• 需要增加流缓冲区中的数据量
• 调用fclose、fflush、fseek、fseeko以及fsetpos时
转自http://haoyuanliu.github.io/2016/11/17/UNIX%E7%BC%96%E7%A8%8B%E4%B9%8B%E5%86%B2%E6%B4%97%E5%86%85%E5%AD%98%E6%B5%81%E4%B8%8Enull%E8%BF%BD%E5%8A%A0%E7%AD%96%E7%95%A5%EF%BC%88APUE-F5-15%EF%BC%89/
//系统的时间函数
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<time.h>
4
5 int main()
6 {
7 time_t t;
8 struct tm *tmp;
9 char buf1[16];
10 char buf2[64];
11 time(&t);
12 tmp = localtime(&t);
13 if(strftime(buf1,16,"time and date:%r ,%a %b %d,%Y",tmp) == 0)
14 printf("buffer length 16 is too small\n");
15 else
16 printf("%s\n",buf1);
17 if(strftime(buf2,64,"time and date: %r,%a,%b,%d,%Y",tmp) == 0)
18 printf("buffer length 64 is too small\n");
19 else
20 printf("%s\n",buf2);
21 exit(0);
22 }