您的位置:首页 > 其它

15_输入/输出函数

2018-01-10 08:38 197 查看


错误报告

errno

errno定义在<errno.h>头文件中,外部变量errno保存库程序中实现定义的错误码,通常被定义为errno.h中以E开头的宏,所有错误码都是正整数。

在linux中使用c语言编程时,errno很有用的。他可以把最后一次调用c的方法的错误代码保留。但是如果最后一次成功的调用c的方法,errno不会改变。因此,只有在c语言函数返回值异常时,再检测errno。errno会返回一个数字,每个数字代表一个错误类型。详细的可以查看头文件。/usr/include/asm/errno.h

如何把errno的数字转换成相应的文字说明?

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <math.h>

int main(void)
{
errno = 0;
int s = sqrt(-1);
if (errno) {
printf("errno = %d\n", errno); // errno = 33
perror("sqrt failed"); // sqrt failed: Numerical argument out of domain
printf("error: %s\n", strerror(errno)); // error: Numerical argument out of domain
}

return 0;


strerror
strerror定义在<string.h>中,

char *strerror(int errno)


函数strerror返回一个错误消息字符串的指针,其内容是由实现定义的,字符串不能修改,但可以在后续调用strerror函数时覆盖。

perror

perror定义在<stdio.h>中:

void perror(const char *s)


perror ( )用来将上一个函数发生错误的原因输出到标准错误(stderr),参数s 所指的字符串会先打印出,后面跟一个分号和一个空格,然后打印出一条用于解释errno当前错误代码的信息。


终止执行

exit函数用于终止一个程序的执行,它的原型定义在stdlib.h,其定义:

void exit(int status);


exit用于在程序运行的过程中随时结束程序,exit的参数是返回给OS的。main函数结束时也会隐式地调用exit函数。

预定义符号EXIT_SUCCESS 和 EXIT_FAILURE分别表示程序终止成功还是失败。

经常在调程序发现错误无法继续执行时,先调用perror函数再调用exit函数退出。


I/O概念


就C程序而言,所有的I/O操作只是简单地从程序移进或移出字节的事情。因此,毫不惊奇的是,这种字节流便被称为流(stream)。程序只需要关心创建正确的输出字节数据,以及正确地解释从输入读取的字节数据。特定I/O设备的细节对程序员是隐藏的。

简单地说,流是对信息的一种抽象。C系统在处理文件(文本文件和二进制文件)时,并不区分类型,都看成是字符流,按字节进行处理。

当一个程序启动时,标准输入、输出、出错三个流就已经被自动打开,并对应到默认的物理终端。这三个标准I/O流通过预定义(stdio.h)文件指针stdin,stdout,stderr加以引用当一个进程正常终止时(直接调用exit(),或从main返回)所有打开的标准I/O流都会被关闭,所有带未写缓冲数据的I/O流都会被冲洗。

注意: 

在main()中return(expr)等价于exit(expr),而exit则调用fclose()关闭每个文件描述符并刷洗对应缓存。 

在Linux的应用程序中,通常用文件描述符0,1,2与标准输入,标准输出,标准出错输出相关联。为符合POSIX规范,在中,0,1,2分别被替换成常量符号STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。


缓冲区

 

缓冲文件系统是为了减少 系统调用(system call)read write的调用频率,降低操作系统负担。

 

缓冲文件系统,系统自动在内存中为每一个正在使用的文件开辟一个缓冲区,从内存向磁盘输出数据或从磁盘读取数据是,都是先放到缓冲区。

 

标准I/O提供了三种类型的缓存:
全缓存。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻在磁盘上的文件通常是由标准I/O库实施全缓存的。

行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I/O操作。

不带缓存。标准I/O库不对字符进行缓存。如果用标准I/O函数写若干字符到不带缓存的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。

ANSI C要求下列缓存特征:

(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
(2) 标准出错决不会是全缓存的。 


二进制流和文本流

流有最小的信息单元就是二进制位,含有最小的信息包就是字节,C标准库提供两种类型的流:二进制流(binary stream)和文本流(text stream)。

二进制流是有未经处理的字节构成的序列;

文本流的有些特性在不同的系统中可能不同。其中之一就是文本行的最大长度,标准规定至少允许254个字符。另一个可能不同的特性就是文本行的结束方式,在MS-DOS中,文本文件约定以一个回车符和一个换行符结尾,但是在UNIX系统中只使用一个换行符结尾。


文件

FILE是一种数据结构,定义在stdio.h文件中,用于访问一个流,如果同时激活了几个流,每个流都会有一个FILE与之关联。

对于C程序,运行时系统必须提供至少三个流:标准输入,标准输出,标准错误,分别用stdin,staout和stderr来表示,它们都是一个指向FILE结构的指针,标准输入是缺省情况下的输入来源,标准输出是缺省的输出设置。标准错误是写入错误信息的地方,perror函数把它的输出也写到这个地方。

标准I/O常量

stdio.h中还定义了众多的输入和输出关联的常量,如:

EOF:表示文件末尾。

FILENAME_MAX:表示一个字符数组应该多大以便容纳编译器所支持的额最长合法文件名。


流I/O总览


文件I/O的一般情况

1.程序必须为同时处于活动状态的每个文件声明一个指针变量,其类型为FILE*。这个指针指向FILE结构,当它处于活动状态时由流使用。

2.流通过调用fopen函数打开,为了打开一个流,必须指定需要访问的文件或设备以及它们的访问方式(例如:读,写,既读又写)。fopen和操作系统验证文件或者设备确实存在并初始化FILE结构。

3.根据指定的方式对文件进行操作。

4.调用fclose函数关闭流。关闭流可以防止与它相关联的文件被再次访问,保证任何存储于缓冲区的数据被正确读写的文件,并且释放FILE结构使它可以用于另外的文件。

标准的I/O流更简单,因为它们不需要关闭或者打开。

I/O函数以三种基本的形式处理数据:单个字符,文本行和二进制数据。对于每种形式都有一组特定的函数对它们进行处理。 

执行字符、文本行和二进制数据I/O的函数:
数据类型
输入
输出
描述
字符
getchar
putchar
读取(写入)单个字符
文本行
gets/scanf
puts/printf
文本行未格式化的输入(输出)/格式化的输入(输出)
二进制数据
fread
fwrite
读取(写入)二进制数据
 输入/输出函数家族:
家族名
目的
可用于所有流
只用于stdin和stdout
内存中的字符串
getchar
字符输入
fgetc, getc
getchar

putchar
字符输出
fputc, putc
putchar

gets
文本行输入
fgets
gets

puts
文本行输出
fputs
puts

scanf
格式化输入
fscanf
scanf
sscanf
printf
格式化输出
fprintf
printf
sprintf
①对指针使用下标引用或间接访问操作从内存获得一个字符(或向内存写入一个字符)。

②使用strcpy函数从内存读取文本行(或向内存写入文本行)。 


打开流

fopen函数打开一个特定的文件,并把一个流和这个文件相关联。原型如下: 

FILE *fopen(const char *name , const char *mode);


 name是要打开的文件或设备名,fopen把文件名作为一个字符串,mode用于只读、只写还是既读又写,以及文本流还是二进制流。

 
读取
写入
添加
文本
"r"
"w"
"a"
二进制
"rb"
"wb"
"ab"
 

 如果一个文件打开用于读取,那么必须是原先已经存在的;如果用于写入,如果原先已存在,原来的内容就会被删除,如果不存在就会创建一个新文件。如果打开用于添加的文件不存在,那么将被创建,如果已经存在,原先的内容也不会被删除。

在mode中添加“a+”表示该文件打开用于更新,并且流既允许读又允许写。但是,如果已经读了一些数据,在写入数据之前要调用一个文件定位函数(fseek、fsetpos、rewind)。如果在写入数据之后又想读取,必须调用fflush函数或文件定位函数之一。 

如果fopen函数执行成功,返回一个指向FILE结构的指针,代表这个新创建的流。如果执行失败,返回一个NULL指针。应始终检测fopen的返回值。 

freopen函数用于打开(或重新打开)一个特定的文件流,原型如下:

FILE* freopen(const char* filename, const char* name, FILE* stream);


最后一个参数是需要打开的流,然后用指定的文件和模式重新打开这个流。如果打开失败,函数返回NULL指针,如果成功,则返回第三个参数值。


 关闭流

关闭流是用函数fclose关闭的,原型如下:

int fclose(FILE* f);


对于输出流,fclose函数在文件关闭之前刷新缓冲区。如果执行成功,fclose返回零值,否则返回EOF。


字符I/O

字符输入是由getchar家族执行的,原型如下: 

int fgetc(FILE* stream);
int getc(FILE* stream);
int getchar(void);


 

需要操作的流作为参数传给getc和fgetc,但getchar始终从标准输入读取。每个函数从流读取一个字符,并作为返回值。如果流中没有字符,就返回EOF。

把单个字符写入到流中,使用putchar家族,原型如下:

int fputc(int character, FILE* stream);
int putc(int character, FILE* stream);
int putchar(int character);


如果由于任何原因(如写入到一个已被关闭的流)导致函数失败,它们就返回EOF。

 


字符I/O宏

fgetc和fputc都是真正的函数,但getc、putc、getchar和putchar都是通过#define定义的宏。


撤销字符I/O

int ungetc(int character, FILE* stream);


ungetc把一个先前读入的字符退回到流中,这样它还可以重新被读入。

注意:把字符退回到流中和写入到流中并不相同。与一个流相关联的外部储存并不受ungetc的影响。“退回”字符和流的当前位置有关,所以如果用fseek、fsetpos或rewind函数改变了流的位置,所有退回的字符都将被丢弃。


未格式化的行I/O

char *fgets(char *buffer, int buffer_size, FILE *stream);
char *gets(char *buffer);

int fputs(char const *buffer, FILE *stream);
int puts(char const *buffer);


fgets从stream流读取字符把它们复制buffer中。当读取到一个换行符并存储到缓冲区后就不再读取。如果buffer内字符数达到buffer_size-1时也停止读取。最后都会在缓冲区数据的末尾添加一个'\0'字符。

如果未读取字符就到了文件尾,缓冲区未修改,fgets函数返回NULL指针。否则fgets返回指向buffer的指针。返回值通常只用于检查是否到达文件尾。

传递给fputs的缓冲区必须包含一个以'\0'结尾的字符串,如果它不包含换行符就不会写入换行符。如果写入出现错误,fputs返回EOF,否则返回一个非负值。

如果函数需要计数被复制的行的数目,太小的缓冲 区将产生一个不正确的计数,因为一个长行可能会被分成数段进行读取。

当gets读取一行输入时,它并不在缓冲区中存储结尾的 换行符。当puts写入一个字符串时,它在字符串写入之后向输出再添加一个换行符。

gets函数没有缓冲区长度参数,也无法判断缓冲区的长度。如果一个长输入行读到一个短的缓冲区,多出来的字符将被写入到缓冲区后面的内存位置,这将破坏一个或多个不相关变量的值。所以gets函数几乎没有实际价值。


格式化的行I/O

scanf和printf函数家族并不仅限于单行。也可以在行的一部分或多行上执行I/O操作。


scanf家族

int fscanf(FILE *stream, char const *format, ... );//从stream流读取
int scanf(char const *format, ... ); //从标准输入读取
int sscanf(char const *string, char const *format, ... );
//从string读取


当格式化字符串到达末尾或读取的输入不再匹配格式字符串所指定的类型时,输入停止。被转换的输入值的数目作为函数的返回值。如果未转换任何输入值就到达尾部,函数返回常量EOF。


printf家族

int fprintf(FILE *stream, char const *format, ... );//任何输出流
int printf(char const *format, ... ); //输出到标准输出
int sprintf(char *buffer, char const *format, ... );
//'\0'结尾的字符串储存到指定的buffer缓冲区而不是写入到流中


返回值是实际打印或储存的字符数。

sprintf是一个潜在的错误根源。如果输出结果很长溢出缓冲区时,就可能改写缓冲区后面的内存数据。


 二进制I/O

把数据写入到文件效率最高的方法是用二进制形式写入。二进制输出避免了在 数值转换为字符串过程中涉及的开销和精度损失。

fread函数用于读取二进制数据,fwrite用于写入二进制数据。

size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
size_t fwrite(void *buffer, size_t size, size_t count, FILE *stream);


buffer是一个指向用于保存数据的内存位置的指针,size是缓冲区中每个元素的字节数,count是读取或写入的元素数,stream是数据读取或写入的流。

函数返回值是实际读取或写入的元素(非字节)数目。如果输入遇到文件尾或输出出现错误,这个数字可能比请求的元素数要小。


刷新和定位函数

fflush它迫使一个输出流的缓冲区内的数据进行物理写入,不管它是不是已经写满。

int fflush(FILE *stream);


例如,调用fflush保证调试信息实际打印出来,而不是保存在缓冲区直到以后才打印。

一般后面写入的数据在文件中的位置是以前所有写入数据的后面。C同时支持随机访问I/O,通过在读取或写入先前定位到文件中需要的位置来实现。

long ftell(FILE *stream);
int fseek(FILE *stream, long offset, int from);


ftell函数返回流当前位置,也就是,下一个读取或写入将要开始的位置距离起始位置的偏移量。在二进制流中,这个值就是当前位置距离文件其实位置之间的字节数。

ftell的返回值总是可以用于fseek函数中,作为距离起始位置偏移量。

定位到文件尾之后并进行写入将扩展这个文件。定位到文件尾之后并进行读取将导致返回一条“到达文件尾”的消息。在二进制中,从SEEK_END进行定位可能不被支持。在文本流,如果from是SEEK_CUR或SEEK_END,offset必须是零。如果from是SEEK_SET,offset必须是一个从同一个流中以前调用ftell返回的值。
符号
定位
SEEK_SET
从流起始位置起offset个字节,offset非负值。
SEEK_CUR
从流当前位置起offset个字节,offset可正可负。
SEEK_END
从流尾部位置起offset个字节,offset可正可负。如果正只,定位到文件尾的后面。
用fseek改变流的位置副作用:

1.行末指示字符被清除。

2.如果在fseek之前使用ungetc,那么这个退回的字符会丢弃。

其他函数,限制更严:

void rewind(FILE *stream);
int fgetpos(FILE *stream, fpos_t *position);
int fsetpos(FILE *stream, fpos_t const *position);


rewind函数将读/写指针设置回指定流的起始位置,同时清除流的错误提示标志。fgetpos和fsetpos分别是ftell和fseek的替代方案,fgetpos用一个指向fpos_t的指针存储文件的当前位置,fsetpos把文件位置设置为存储在这个位置的值。


改变缓冲方式

只有当指定的流被打开但还没有在它上面执行任何其他操作前才能被调用。

void setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf, int mode, size_t size);


setbuf设置另一个长度为BUFSIZ(stdio.h)的字符数组对流进行缓冲。如果用一个NULL参数调用,setbuf将关闭流的所有缓冲方式。

如果在流关闭之前,程序的执行离开了数组声明所在的代码块,流就会继续使用这块内存,但此时可能分配给其他程序使用。

setvbuf更为通用。mode用于指定缓类型。IOFBF指定一个完全缓冲的流,IONBF指定一个不缓冲的流,_IOLBF指定一个行缓冲流。行缓冲流就是每当一个换行符写入到缓冲区时,缓冲区便进行刷新。

buf和size指定使用的缓冲区。buf为NULL,size必须为0。最好用一个长度为BUFSIZ的字符数组作为缓冲区。


流错误函数

int feof(FILE *stream);
int ferror(FILE *stream);
void clearerr(FILE *stream);


如果流当前处于文件尾,feof返回真。这个状态可以通过对流执行fseek、rewind、fsetpos来清除。ferror报告流的错误状态,出现任何读写错误返回真。clearerr对指定流的错误标志进行重置。

FILE *tmpfile(void);


这个函数创建一个文件,当文件被关闭或程序终止时这个问价自动删除。该文件以wb+模式打开,可用于二进制和文本数据。

临时文件的名字可用tmpnam函数创建:

char *tmpnam(char *name);



文件操纵函数

int remove(char const *filename);
int rename(char const *oldname, char const *newname);


如果执行成功,返回零值,失败返回非零值。

remove删除一个指定文件。若文件处于打开状态,结果取决编译器。

rename修改一个文件的名字,如果函数失败,文件仍可用原名字。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐