Unix环境高级编程:第五、六章

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 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值