UNIX环境高级编程——标准I/O库

5.1 引言

本章讲述标准I/O库,这个库由ISO C标准说明。

5.2 流和FILE对象

  • 对于标准I/O库,它们的操作是围绕(stream)进行的,当用标准I/O库打开或创建一个文件时,就使一个流与一个文件关联;
  • 标准I/O文件流可用于单字节或多字节(“宽”)字符集,流的定向决定了所读、写的字符是单字节还是多字节的,当一个流最初被创建时,它并没有定向。

fwide函数可用于设置流的定向:

#include <stdio.h>
#include <wchar.h>int fwide(FILE *fp, int mode);// 返回值:若流是宽定向的,返回正值;若流是字节定向的,返回负值;若流是未定向的,返回0 
  • 如若mode参数值为负,fwide将试图使指定的流是字节定向的;
  • 如若mode参数值为正,fwide将试图使指定的流是宽定向的;
  • 如若mode参数值为0,fwide将不试图设置流的定向,但返回标识该流定向的值。

注意:

  • fwide不改变已定向流的定向;
  • fwide无出错返回,在调用fwide前先清除errno,返回时检查errno的值。

当打开一个流时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象包含了标准I/O库为管理该流需要的所有信息,包括用于实际I/O的文件描述符、指向用于该流缓冲区的指针加粗样式缓冲区的长度、当前在缓冲区中的字符数以及出错标志等。

5.3 标准输入、标准输出和标准错误

对一个进程预定义了3个流,并且这3个流可以自动地被进程使用,它们分别是:标准输入标准输出标准错误,分别通过预定义文件指针stdinstdoutstderr加以引用。

5.4 缓冲

标准I/O提供了以下3种类型的缓冲:

  • 全缓冲:在填满标准I/O缓冲区后才进行实际I/O操作;
  • 行缓冲:在输入和输出中遇到换行符时,标准I/O库执行I/O操作;
  • 不带缓冲:标准I/O库不对字符进行缓冲存储。

可以调用setbufsetvbuf来更改缓冲类型:

#include <stdio.h>void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);// 返回值:若成功,返回0;若出错,返回非0
  • 可使用setbuf函数打开或关闭缓冲机制,参数buf必须指向一个长度位BUFSIZ的缓冲区;为了关闭缓冲,将buf设置位NULL
  • setvbuf可以根据mode精确说明所需的缓冲类型:
mode 缓冲
_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不带缓冲

在这里插入图片描述
可以使用fflush强制冲洗一个流:

#include <stdio.h>int fflush(FILE *fp);// 返回值:若成功,返回0;若出错,返回EOF

此函数使该流所有未写的数据都被传送至内核,特别的,如若fpNULL,则此函数将导致所有输出流被冲洗。

5.5 打开流

下列3个函数打开一个标准I/O流:

#include <stdio.h>FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);// 3个函数的返回值:若成功,返回文件指针;若出错,返回NULL

这3个函数的区别如下:

  • fopen函数打开路径名为pathname的一个指定的文件;
  • freopen函数在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流;若该流已经定向,则使用freopen清除该定向;此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准错误;
  • fdopen函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合;此函数常用于由创建管道和网络通信通道函数返回的描述符。

type参数指定对该I/O流的读、写方式,共有15种不同的值:
在这里插入图片描述

  • 除非流引用终端设备,否则按系统默认,流被打开时是全缓冲的;
  • 若流引用终端设备,则该流是行缓冲的。

调用fclose关闭一个打开的流:

#include <stdio.h>int fclose(FILE *fp);// 返回值:若成功,返回0;若出错,返回EOF
  • 在该文件被关闭之前,冲洗缓冲中的输出数据;
  • 缓冲区中的任何输入数据被丢弃;
  • 如果标准I/O库已经为该流自动分配了一个缓冲区,则释放此缓冲区;
  • 当一个进程正常终止时,则所有带未写缓冲数据的标准I/O流都被冲洗,所有打开的标准I/O流都被关闭。

5.6 读和写流

一旦打开了流,则可在3种不同类型的非格式化I/O中进行选择,对其进行读、写操作:

  • 每次一个字符的I/O:一次读或写一个字符,如果流是带缓冲的,则标准I/O函数处理所有缓冲;
  • 每次一行的I/O:如果想要一次读或写一行,则使用fgetsfputs,每行都以一个换行符终止,当调用fgets时,应说明能处理的最大行长;
  • 直接I/Ofreadfwrite函数支持这种类型的I/O,每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度,这两个函数常用于从二进制文件中每次读或写一个结构。

1. 输入函数

以下3个函数可用于一次读一个字符:

#include <stdio.h>int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);// 3个函数的返回值:若成功,返回下一个字符;若已到达文件尾端或出错,返回EOF
  • 函数getchar等同于getc(stdin)
  • getc可被实现为宏,而fgetc不能实现为宏;
  • 在<stdio.h>中的常量EOF被要求是一个负值,其值通常是-1。

不管是出错还是到达文件尾端,这3个函数都返回同样的值,为了区分这两种不同的情况,必须调用ferrorfeof

#include <stdio.h>int ferror(FILE *fp);
int feof(FILE *fp);// 两个函数的返回值:若条件为真,返回非0(真);否则,返回0(假)

在大多数实现中,为每个流在FILE对象中维护了两个标志:

  • 出错标志;
  • 文件结束标志。

调用clearerr可以清除这两个标志:

#include <stdio.h>void clearerr(FILE *fp);

从流中读取数据以后,可以调用ungetc将字符再压送回流中:

#include <stdio.h>int ungetc(int c, FILE *fp);// 返回值:若成功,返回c;若出错,返回EOF

2. 输出函数

#include <stdio.h>int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);// 3个函数返回值:若成功,返回c;若出错,返回EOF
  • putchar(c)等同于putc(c, stdout)
  • putc可被实现为宏,而fputc不能实现为宏。

5.7 每次一行I/O

下面两个函数提供每次输入一行的功能:

#include <stdio.h>char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);// 两个函数返回值:若成功,返回buf;若已到达文件尾端或出错,返回NULL
  • gets从标准输入读,fgets从指定的流读;
  • fgets必须指定缓冲区的长度n,此函数一直读到下一个换行符为止,但不超过n-1个字符,读入的字符被送入缓冲区;
  • 读缓冲区以null字节结尾。

fputsputs提供每次输出一行的功能:

#include <stdio.h>int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);// 两个函数返回值:若成功,返回非负值;若出错,返回EOF
  • fputs将一个以null字节终止的字符串写到指定的流,尾端的终止符null不写出;
  • puts将一个以null字节终止的字符串写到标准输出,终止符不写出,但puts随后又将一个换行符写到标准输出。

5.8 标准I/O的效率

  • exit函数将会冲洗任何未写的数据,然后关闭所有打开的流;
  • 系统调用与普通的函数调用相比需要花费更多的时间;
  • 标准I/O库与直接调用readwrite函数相比并不慢很多。

5.9 二进制I/O

二进制I/O操作,一次读或写一个完整的结构,下列两个函数用于执行I/O操作:

#include <stdio.h>size_t fread(void *restrict buf, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);// 两个函数的返回值:读或写的对象数
  • 对于读,如果出错或到达文件尾端,则返回值可以少于nobj,在这种情况下,应调用ferrorfeof以判断究竟是哪一种情况;
  • 对于写,如果返回值少于所要求的nobj,则报错。

5.10 定位流

有3种方法定位标准I/O流:

  • ftellfseek函数;
  • ftellofseeko函数;
  • fgetposfsetpos函数。
#include <stdio.h>long ftell(FILE *fp);// 返回值:若成功,返回当前文件位置指示;若出错,返回-1L
int fseek(FILE *fp, long offset, int whence);// 若成功,返回0;若出错,返回-1
void rewind(FILE *fp);
  • 对于一个二进制文件,其文件位置指示器是从文件起始位置开始度量,并以字节为度量单位的;
  • fseekwhence参数的可选值:SEEK_SET表示从文件的起始位置开始,SEEK_CUR表示从当前文件位置开始,SEEK_END表示从文件的尾端开始;
  • 为了定位一个文本文件,whence一定要是SEEK_SET,而且offset只能有两种值:0(后退到文件的起始位置)或是对该文件的ftell所返回的值;
  • rewind函数将一个流设置到文件的起始位置。
#include <stdio.h>off_t ftello(FILE *fp);// 返回值:若成功,返回当前文件位置;若出错,返回(off_t)-1
int fseeko(FILE *fp, off_t offset, int whence)// 返回值:若成功,返回0;若出错,返回-1
#include <stdio.h>int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos)// 两个函数返回值:若成功,返回0;若出错,返回非0
  • fgetpos将文件位置指示器的当前值存入由pos指向的对象中,在以后调用fsetpos时,可以使用此值将流重新定位至该位置。

5.11 格式化I/O

1. 格式化输出

格式化输出是由5个printf函数来处理的:

#include <stdio.h>int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);// 3个函数返回值:若成功,返回输出字符数;若输出出错,返回负值
int sprintf(char *restrict buf, const char *restrict format, ...);// 返回值:若成功,返回存入数组的字符数;若编码出错,返回负值
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);// 返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值
  • printf将格式化数据写到标准输出;
  • fprintf写至指定的流;
  • dprintf写至指定的文件描述符;
  • sprintf将格式化的字符送入数组buf中,在该数组的尾端自动添加一个null字节,但该字符不包括在返回值中
  • snprintfsprintf多了一个参数n,它指明了缓冲区长度,超过缓冲区尾端写的所有字符都被丢弃。

每个参数按照转换说明编写,转换说明以百分号%开始,除转换说明外,格式字符串中的其他字符将按原样,不经任何修改被复制输出。一个转换说明有4个可选择的部分,下面将它们都展示于方括号中:
%[flags][fldwidth][precision][lenmodifier]convtype

  • flags标志如下图所示:
    在这里插入图片描述
  • fldwidth说明最小字段宽度,转换后参数字符数若小于宽度,则多余字符位置用空格填充,字段宽度是一个非负十进制数,或是一个星号(*);
  • precision说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数、字符串转后最大字节数,精度是一个点(.),其后跟随一个可选的非负十进制数或一个星号(*);
  • lenmodifier说明参数长度,其可能值如下图:
    在这里插入图片描述
  • convtype不是可选的,它控制如何解释参数,下图列出了各种转换类型字符:
    在这里插入图片描述
    下列5种printf族的变体类似于上面的5种,但是可变参数...)替换成了arg
#include <stdarg.h>
#include <stdio.h>int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
int vdprintf(int fd, const char *restrict format, va_list arg);// 所有3个函数返回值:若成功,返回输出字符数;若输出出错,返回负值
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);// 函数返回值:若成功,返回存入数组的字符数;若编码出错,返回负值
int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);// 函数返回值:若缓冲区足够大,返回存入数组的字符数;若编码出错,返回负值

2. 格式化输入

执行格式化输入处理的是3个scanf函数:

#include <stdio.h>int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);// 3个函数返回值:赋值的输入项数;若输入出错或在任一转换前已到达文件尾端,返回EOF
  • scanf族用于分析输入字符串,并将字符序列转换成指定类型的变量;
  • 在格式之后的各参数包含了变量的地址,用转换结果对这些变量赋值;

格式说明控制如何转换参数,以便对它们赋值,转换说明以百分号%字符开始,除转换说明和空白字符外,格式字符串中的其他字符必须于输入匹配。一个转换说明有3个可选择的部分,下面将它们都示于方括号中:
%[*][fldwidth][m][lenmodifier]convtype

  • 可选择的星号(*)用于抑制转换,按照转换说明的其余部分对输入进行转换,但转换结果并不存放在参数中;
  • fldwidth说明最大宽度(即最大字符数);
  • lenmodifier说明要用转换结果赋值的参数大小,由printf函数族支持的长度修饰符同样得到scanf族函数的支持;
  • convtype说明转换类型,scanf族函数支持的转换类型如下图:
    在这里插入图片描述
    printf族相同,scanf族也使用由<stdarg.h>说明的可变长度参数表:
#include <stdarg.h>
#include <stdio.h>int vscanf(const char *restrict format, va_list arg);
int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);// 3个函数返回值:指定的输入项目数;若输入出错或在任一转换前文件结束,返回EOF

5.12 实现细节

每个标准I/O流都有一个与其相关联的文件描述符,可以对一个流调用fileno函数以获得其描述符:

#include <stdio.h>int fileno(FILE *fp);// 返回值:与该流相关联的文件描述符

5.13 临时文件

ISO C标准I/O库提供了两个函数来创建临时文件:

#include <stdio.h>char *tmpnam(char *ptr);// 返回值:指向唯一路径名的指针
FILE *tmpfile(void);// 返回值:若成功,返回文件指针;若出错,返回NULL
  • tmpnam函数产生一个与现有文件名不同的一个有效路径名字符串,每次调用它时,都产生一个不同的路径名;
  • ptrNULL,则所产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回,后续调用tmpnam时,会重写该静态区;
  • ptr不是NULL,则认为它应该是指向长度至少是L_tmpnam个字符的数组,所产生的路径名存放在该数组中,ptr也作为函数值返回;
  • tmpfile创建一个临时二进制文件(类型wb+),在关闭该文件或程序结束时将自动删除这种文件。

Single UNIX Specification为处理临时文件定义了另外两个函数,即mkdtempmkstemp

#include <stdlib.h>char *mkdtemp(char *template);// 返回值:若成功,返回指向目录名的指针;若出错,返回NULL
int mkstemp(char *template);// 返回值:若成功,返回文件描述符;若出错,返回-1
  • mkdtemp函数创建了一个目录,该目录有一个唯一的名字;
  • mkstemp函数创建了一个文件,该文件有一个唯一的名字;
  • 名字通过template字符串进行选择,这个字符串是后6位设置为XXXXXX的路径名,函数将这些占位符替换成不同的字符来构建一个唯一的路径名,如果成功的话,这两个函数将修改template字符串反映临时文件的名字;
  • mkdtemp函数创建的目录使用下列访问权限位集:S_IRUSR | S_IWUSR | S_IXUSR
  • mkstemp函数创建的文件使用访问权限位:S_IRUSR | S_IWUSR
  • mkstemp创建的临时文件并不会自动删除。

5.14 内存流

有3个函数可用于内存流的创建,第一个是fmemopen函数:

#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);// 返回值:若成功,返回流指针;若错误,返回NULL
  • buf参数指向缓冲区的开始位置,size参数指定了缓冲区大小的字节数,如果buf参数为空,fmemopen函数分配size字节的缓冲区,在这种情况下,当流关闭时缓冲区会被释放;
  • type参数控制如何使用流,可能取值如下:
    在这里插入图片描述

用于创建内存流的其他两个函数分别是open_memstreamopen_wmemstream

#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep);#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);// 两个函数的返回值:若成功,返回流指针;若出错,返回NULL

open_memstream函数创建的流是面向字节的,open_wmemstream函数创建的流是面向宽字节的,这两个函数与fmemopen函数的不同在于:

  • 创建的流只能写打开;
  • 不能指定自己的缓冲区,但可以分别通过bufpsizep参数访问缓冲区地址和大小;
  • 关闭流后需要自行释放缓冲区;
  • 对流添加字节会增加缓冲区大小。

5.15 实例代码

chapter5

查看全文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2265692.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章:

在这里插入图片描述

UNIX环境高级编程——标准I/O库

5.1 引言
本章讲述标准I/O库,这个库由ISO C标准说明。
5.2 流和FILE对象
对于标准I/O库,它们的操作是围绕流(stream)进行的,当用标准I/O库打开或创建一个文件时,就使一个流与一个文件关联;标……

LeetCode412_412. Fizz Buzz

LeetCode412_412. Fizz Buzz
一、描述
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:
answer[i] “FizzBuzz” 如果 i 同时是 3 和 5……

计算机网络面试八股文攻略(二)—— TCP 与 UDP

一、基础概念
TCP 与 UDP 是活跃于 运输层 的数据传输协议
TCP:传输控制协议
(Transmission Control Protocol)–提供面向连接的,可靠的数据传输服务。具体来说就是一种要建立双端连接才能发送数据,能确保传输可靠的……

《疯狂Java讲义》读书笔记6

数据结构,对循环队列,双端队列的总结:
http://t.csdn.cn/kgZcI
刷题:
http://t.csdn.cn/YMc3M
异常处理
对于构造大型、健壮、可维护的应用,错误处理是整个应用要考虑的重要方面。
在Java中,Exceptio……

C++编程法则365条一天一条(358)copy elision(返回值优化NVO和具名返回值优化NRVO)

参考:https://en.cppreference.com/w/cpp/language/copy_elision
Elision 是一个英文单词,指的是省略、删节或者忽略的意思。在C中,Copy elision则是编译器优化技术的一种,该技术可以避免进行不必要的复制和移动操作,……

Python3 os.renames() 方法、Python3 os.tmpnam() 方法

Python3 os.renames() 方法 概述
os.renames() 方法用于递归重命名目录或文件。类似rename()。
语法
renames()方法语法格式如下:
os.renames(old, new)参数 old — 要重命名的目录 new –文件或目录的新名字。甚至可以是包含在目录中的文件,或者完……

【FPGA】多功能ALU

目录
实验要求
源代码 顶层模块 数据输入模块 ALU运算模块 结果处理模块 扫描数码管模块 扫描数码管顶层 分频器 数码管显示
仿真代码
结构层图
管脚配置 实验板卡:xc7a100tlc sg324-2L,共20个开关
实验要求
通过高低位控制,实现32位数……

5.7 维吉尼亚密码

任务描述
本关任务:编写程序,通过维吉尼亚密码将密文解密成文本。
相关知识
维吉尼亚密码——解密
凯撒密码的加密强度是很低的,只需简单地统计字频就可以破译。人们在单一凯撒密码的基础上扩展出多表密码,称为维吉尼亚密码。……

C++在Linux用cmake上构建os动态链接库

ps:使用CMake来构建动态链接库可以让项目更加易于管理和构建。 下面我将演示如何使用CMake来构建一个名为“mylib”的动态链接库。
首先,创建一个名为“mylib”的文件夹,并在其中创建一个名为“mylib.h”的头文件和一个名为“mylib.cpp”的源文件
myli……

云原生网络之微隔离

本博客地址:https://security.blog.csdn.net/article/details/130044619
一、微隔离介绍
1.1、微隔离概念
在主体执行动作时,对主体权限和行为进行判断,最常见的是网络访问控制,也就是零信任网络访问(ZTNA&#xff……

【hello Linux】环境变量

目录 1. 环境变量的概念 2. 常见的环境变量 3. 查看环境变量 4. 和环境变量相关的命令 5. 环境变量的组织方式 6. 通过代码获取环境变量 7. 通过系统调用获取环境变量 Linux🌷 在开始今天的内容之前,先来看一幅图片吧! 不知道你们是否和我一……

【Linux基础】常用命令整理

ls命令
-a选项,可以展示隐藏的文件和文件夹-l选项,以列表形式展示内容-h,需要和-l搭配使用,可以展示文件的大小单位ls -lah等同于la -a -l -h
cd命令(change directory)
语法:cd [Linux路径]……

客快物流大数据项目(一百一十二):初识Spring Cloud

文章目录
初识Spring Cloud
一、Spring Cloud简介
二、SpringCloud 基础架构图…

C和C++中的struct有什么区别

区别一: C语言中: Struct是用户自定义数据类型(UDT)。 C语言中: Struct是抽象数据类型(ADT),支持成员函数的定义。
区别二:
C中的struct是没有权限设置的&#xff0c……

docker的数据卷详解

数据卷 数据卷是宿主机中的一个目录或文件,当容器目录和数据卷目录绑定后,对方修改会立即同步
一个数据卷可以同时被多个容器同时挂载,一个容器也可以被挂载多个数据卷
数据卷作用:容器数据持久化 /外部机器和容器间接通信 /容器……

13、Qt生成dll-QLibrary方式使用

Qt创建dll,使用QLibrary类方式调用dll
一、创建项目
1、新建项目->其他项目->Empty qmake Project->Choose 2、输入项目名,选择项目位置,下一步 3、选择MinGW,下一步 4、完成 5、.pro中添加TEMPLATE subdirs&#xff……

基于mapreduce 的 minHash 矩阵压缩

Minhash作用: 对大矩阵进行降维处理,在进行计算俩个用户之间的相似度。
比如: 俩个用户手机下载的APP的相似度,在一个矩阵中会有很多很多的用户要比较没俩个用户之间的相似度是一个很大的计算任务 如果首先对这个矩阵降维处理&am……

关于hashmap使用迭代器的问题

keySet获得的只是key值的集合,valueSet获得的是value集合,entryset获得的是键值对的集合。 package com.test2.test;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;public class mapiterator……

Hadoop入口FileSystem HDFS操作 本地文件合并到HDFS和HDFS文件合并

Hadoop 文件API的起点是FileSystem类。这是一个与文件系统交互的抽象类。存在不同的具体实现子类来处理HDFS和本地文件系统。
HDFS接口的FileSystem对象:
Configuration conf new Configuration();
FileSystem hdfs FileSystem.get(conf); HDFS直接操作&#x……

combiner partitioner

combine是在map端进行的,是在patition之后 partitioner也是在map端进行的 combine 适用在每个map端进行简单的合并,同样也是继承Reduce类。…

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注