【UNIX/Liux】标准I/O库【Part 2】

本文是笔者拜读《UNIX环境高级编程》第5章(标准I/O库)的学习笔记。本文的主要内容包括二进制I/O、定位流、格式化I/O、临时文件。文中不仅包含书中的知识点,也包括笔者的理解。

二进制I/O

在执行二进制I/O操作时,通常一次性读写一个完整的数据块。下面两个函数执行二进制I/O操作。
在这里插入图片描述
size表示每个数据项的大小,nmemb表示要读写的数据项数目。
这些函数有以下两种常见的用法:
(1) 读写一个二进制数组。例如,将一个浮点数组的第2~5(从0开始计数)个元素写到一个文件上。

float data[10];
if (fwrite(&data[2], sizeof(float), 4, fp) != 4) {
	perror("fwrite error");
}

(2) 读或写一个结构。

struct {
	short count;
	long total;
	char name[NAMESIZE];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1) {
	perror("fwrite error");
}

二进制I/O只能用于读写在同一系统上的数据。而在如今的异构系统中,常常在一个系统上写数据,在另一个系统上进行处理。这种情况下,freadfwrite可能就不能正常工作,因为:
(1)在一个结构中,同一成员的偏移量可能随编译程序和系统的不同而不同。
(2)多字节整数和浮点值的二进制格式在不同的系统结构间也可能不同。
在不同系统之间交换二进制数据的实际解决方法是使用互认的规范格式。

定位流

3种方法定位标准I/O流。
(1) ftellfseek函数。这些函数假定文件的位置可以存放在一个长整型中。
(2) ftellofseeko函数。使用off_t数据类型表示文件偏移量。
(3) fgetposfsetpos函数。这些函数使用一个抽象数据类型fpos_t记录文件的位置,这种数据类型可以根据需要定义为一个足够大的数。
在这里插入图片描述
ftell返回当前文件位置指示;fseek设置文件位置。文件位置相当于文件偏移量,fseek相当于lseek。使用rewind可将一个流设置到文件的起始位置。

除了偏移量类型以外,ftelloftell相同,fseekofseek相同。

fgetpos将文件位置指示器的当前值存入由pos指向的对象中(功能类似于ftell),调用fsetpos时,可以使用此值将流重新定位到该位置。

格式化I/O

格式化输出

在这里插入图片描述
printf将格式化数据写到标准输出,fprintf写至指定的流,dprintf写至指定的文件描述符,sprintf写至指定的数组(在数组尾端自动加一个'\0')。

为了解决缓冲区溢出问题,引入了指定缓冲区长度的snprintf函数,超过缓冲区尾端的字符都会被丢弃。

格式说明控制其余参数如何编写,如何显示。每个参数按照转换说明编写,转换说明以%开始。除转换说明外,格式字符串中的其它字符将按原样输出。一个转换说明有4个可选择部分:
%[flags][fldwidth][precision][lenmodifier]convtype

flags说明
'将整数按千位分组
-左对齐
+带正负号
(空格)如果第一个字符不是正负号,则在前面加上一个空格
#指定另一种转换格式
0使用0进行填充

fldwidth指定了最小字段宽度。转换后参数字符数若小于宽度,则多余字符用空格填充。字符宽度是一个非负的十进制数,或者*

precision指定了精度。例如整型的数字位数、浮点数的小数位数、字符串的长度。精度表示为.后面跟个可选的非负十进制数或*

宽度和精度字段皆可为*。此时,一个整型参数指定宽度或精度的值,该整型参数正好位于被转换的参数之前。

lenmodifier说明参数长度。

长度修饰符说明
hh将相应的参数按signedunsigned char类型输出
h将相应的参数按signedunsigned short类型输出
l将相应的参数按signedunsigned long类型输出
ll将相应的参数按signedunsigned long long类型输出
jintmax_tuintmax_t
zsize_t
tptrdiff_t
Llong double

convtype是必选的,它控制如何解释参数。

转换类型说明
d,i有符号十进制
o无符号八进制
u无符号十进制
x,X无符号十六进制
f,F双精度浮点数
e,E指数格式双精度浮点数
g,G根据转换后的值解释为f,F,eE
a,A十六进制指数格式双精度浮点数
c字符(若带长度修饰符l,则为宽字符)
s字符串(若带长度修饰符l,则为宽字符)
pvoid*
n到目前为止,此printf调用输出的字符的数目将被写入到指针所指向的带符号整型中
%一个%字符
C宽字符(等效于lc
S宽字符串(等效于ls

常规的转换是按照format...中参数出现的顺序的,一种替代的转换说明语法也允许显式地用%n$序列来表示第n个参数的形式来命名参数(从1开始计数)。这两种语法不能在同一格式说明中混用。如果参数既没有提供字段宽度也没有提供精度,通配符星号的语法就更改为*m$m指明提供值的参数的位置。

vprintf族是printf族的变体,它的可变参数表...被替换成了va_list
例:

#include <stdio.h>

int main() {
	char str[] = "hello";
	char ch = 'z';
	int a = -123;
	int b = 12339783;
	double c = 13.234;
	printf("str: %-10s!\n", str);
	printf("ch: %c!\n", ch);
	printf("a: %+d!\n", a);
	printf("b: %+10d!\n", b);
	printf("c: %.2f!\n", c);
	return 0;
}

运行结果:
在这里插入图片描述

格式化输入

在这里插入图片描述
scanf族用于分析输入字符串,并将字符序列转换成指定类型的变量,格式之后的个参数包含了变量的地址,用转换结果对这些变量赋值。

格式说明控制如何转换参数,以%开始。除转换说明和空白字符外,格式字符串中的其它字符必须与输入匹配,若有一个不匹配,则停止后续处理。
%[*][fldwidth][m][lenmodifier]convtype

*用于抑制转换。按照转换说明的其余部分对输入进行转换,但转换结果并不存放在参数中。

dlfwidth说明最大宽度(最大字符数)。lenmodifier说明要用转换结果赋值的参数大小。由printf函数族支持的长度修饰符同样得到scanf族函数的支持。

convtype字段类似于printf族的转换类型字段,但输入中带符号的可赋予无符号类型。例如输入流中的-1被转换成UINT_MAX

在字段宽度和长度修饰符之间的m是赋值分配符。它可用于%c%s以及%[转换符,迫使内存缓冲区分配空间以接纳转换字符串。

scanf族同样支持显式地命名参数。

scanf的转换类型与printf大概一样。

转换类型说明
[匹配列出的字符序列,以]终止
[^匹配除列出字符以外的所有字符,以]终止

例:
练习scanf函数。

#include <stdio.h>

int main() {
	int a;
	float b;
	char c[100];
	char d;
	int e;

	int n = fscanf(stdin, "%d %f %s %*[a-zA-Z] %c %d", &a, &b, c, &d, &e);
	fprintf(stdout, "get %d variables\n", n);
	fprintf(stdout, "%d %.2f %s station %c %d\n", a, b, c, d, e);
	return 0;
}

运行结果:
在这里插入图片描述
程序中%*[a-zA-Z]表示,如果匹配到了一个或多个字母,则忽略它们(不用将读到的字符串赋值给某个变量),否则函数返回或匹配下一个转换说明。
%*[^a-zA-Z]表示,如果匹配到了一个或多个非字母,则忽略它们,否则函数返回或匹配下一个转换说明。

scanf里的内容真复杂,笔者说不清楚。

实现细节

每个标准I/O流都有一个与其相关联的文件描述符,可以对一个流调用fileno函数以获得其描述符。
在这里插入图片描述
在打印缓冲状态信息之前,先对每个流执行I/O操作,第一个I/O操作通常就造成为该流分配缓冲区。在不同系统中,标准I/O库的实现有所不同,标准定义的是声明(接口)。

当标准输入、标准输出连至终端时,它们是行缓冲的。当将这两个流重定向到普通文件时,它们就是全缓冲的。不论重定向与否,标准错误流始终是不带缓冲的。

临时文件

以下函数创建临时文件。
在这里插入图片描述
在这里插入图片描述
tmpnam函数产生一个与现有文件名不同的一个有效路径名字符串。
sNULL,则产生的路径名存放在静态区中,指向该静态区的指针作为函数的返回值。后续调用tmpnam时,会重写该静态区。
s不是NULL,则认为它指向一个字符数组,所产生的路径名存放在该数组中,返回s
tmpfile使用tmpnam生成的路径名(通常在/tmp/目录下)创建一个临时二进制文件(类型wb+),在关闭该文件或程序结束时会自动删除该文件。

mkdtempmkstemp也能创建临时文件。
在这里插入图片描述
在这里插入图片描述
mkdtemp函数创建了一个目录,该目录有一个唯一的名字;mkstemp函数创建了一个文件,该文件有一个唯一的名字。名字是通过template字符串进行选择的。这个字符串是后6位设置为XXXXXX的路径名,函数将这些占位符替换成不同的字符来构建一个唯一的路径名。

mkdtemp函数创建的目录使用下列访问权限位集:S_IRUSR | S_IWUSR | S_IXUSR,可以调用umask进一步限制这些权限。mkdtemp返回新目录的名字。

mkstemp创建的临时文件使用访问权限位S_IRUSR | S_IWUSRmkstemp创建的临时文件不会被自动删除,必须对它解除链接。
例:
临时文件的创建和关闭

// temp.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
	char buf1[100];
	tmpnam(buf1);
	FILE *fp = tmpfile();
	fprintf(fp, "%s: %s", buf1, "hello");
	rewind(fp);
	fgets(buf1, 100, fp);
	printf("%s\n", buf1);
	fclose(fp);
	
	char buf2[] = "/tmp/testXXXXXX";
	int fd = mkstemp(buf2);
	fp = fdopen(fd, "r+");
	fprintf(fp, "%s: %s", buf2, "hello");
	rewind(fp);
	fgets(buf1, 100, fp);
	printf("%s\n", buf1);
	unlink(buf2);
	fclose(fp);	
	return 0;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值