目录
一、概述
本文主要从介绍Linux 标准I/O操作以及文件I/O操作的主要区别以及使用方法,读完本文,即可以掌握两者之间的区别,也会使用他们在Linux 环境中编程,本文会列举介绍每种IO的接口API,并给出使用例程。
二、API介绍以及使用例程
2.1 什么是标准IO以及文件IO简介
文件I/O:文件I/O称之为不带缓存的IO(unbuffered I/O)。不带缓存指的是每个read,write都调用内核中的一个系统调用。也就是一般所说的低级I/O——操作系统提供的基本IO服务,与os绑定(特定于linix或unix平台)。
标准I/O:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性。标准I/O库处理很多细节。例如缓存分配,以优化长度执行I/O等。标准的I/O提供了三种类型的缓存。
(1)全缓存:当填满标准I/O缓存后才进行实际的I/O操作。
(2)行缓存:当输入或输出中遇到新行符时,标准I/O库执行I/O操作。
(3)不带缓存:stderr就是了。
2.2 两种I/O的区别
文件I/O 又称为低级磁盘I/O,遵循POSIX相关标准。任何兼容POSIX标准的操作系统上都支持文件I/O。
标准I/O被称为高级磁盘I/O,遵循ANSI C相关标准。只要开发环境中有标准I/O库,标准I/O就可以使用。(Linux 中使用的是GLIBC,它是标准C库的超集。不仅包含ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux 下既可以使用标准I/O,也可以使用文件I/O)。
通过文件I/O读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销,标准I/O可以看成是在文件I/O的基础上封装了缓冲机制。先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数。
文件I/O中用文件描述符表现一个打开的文件,可以访问不同类型的文件如普通文件、设备文件和管道文件等。而标准I/O中用FILE(流)表示一个打开的文件,通常只用来访问普通文件。
2.3 什么是文件描述符
Linux中的文件主要分为4种:普通文件、目录文件、链接文件和设备文件。这么的文件系统如何做区分呢?——文件描述符。文件描述符是个非负整数,是个索引值,并指向在内核中每个进程打开文件的记录表。在一个进程启动时,会自动打开3个文件:标准输入、标准输出和标准错误处理,这3个文件分别对应的文件描述符为0、1、2。
2.4 操作API的区别
项目 | 标准I/O操作 | 普通I/O 操作 |
---|---|---|
打开 | fopen,freopen,fdopen | open |
读 | getc,fgetc,getchar,fgets,gets,fread | read |
写 | putc,fputc,putchar,fputs,puts,fwrite | write |
随机存取 | fseek、ftell | lseek |
关闭 | fclose | close |
三、标准I/O操作API使用总结
3.1 打开文件
打开文件主要有几个API,fopen,freopen,fdopen。这里先简单介绍一下“文件”,“文件是指一组相关数据的有序集合”,这个数据集的名称就是文件名。所有文件都是通过流的形式进行输入输出操作的。流是一个数据字节的序列。流入程序的流称为输入流,流出程序的流称为输出流。
流可分为两大类:文本流和二进制流
(1)文本流是一种字符序列,仅包含字符。
(2)二进制流是一种二进制序列,与外设中的字节序列一一对应,不对字节中的内容进行转换,如遇到换行符也不会执行换行操作。
文本又可以分为文本文件和二进制文本文件两大类。
(1)文本文件又称ASCII文件,也称字符文件。这种文件保存时,每个字符对应一个字节,用于存放对应的ASCII码。可通过输出设备显示,是我们能够读懂的内容。
(2)二进制文件是由0和1组成的一段序列,用二进制的方式保存文件内容。虽然二进制也能够输出显示,但是只有机器能够读懂它的意思。
文件指针:
typedef struct
{
short level; /*缓冲区的使用量*/
unsigned flags; /*标志文件状态*/
char fd; /*文件号*/
unsigned char hold; /* 无缓冲区取消字符输入*/
short bsize; /*缓冲区大小默认值512*/
unsigned char *buffer; /*缓冲区指针*/
unsigned ar *curp; /*无活动指针*/
unsigned istemp; /*草稿文件标识*/
short token; /*做正确性检测*/
}FILE;
fopen()函数是ANSIC规定的标准输入/输出函数库中的函数。打开文件的操作就是创建一个流。
#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *fropen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int filedes, const char *type);
返回值:成功返回文件指针,出错返回NULL
fopen打开一个指定的文件。
freopen在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭流。若该流已经定向,则freopen清除该定向。此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。
fdopen获取一个现有的文件描述符,并使一个标准的I/O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数返回的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,所以必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相关联。
fopen和freopen是ISO C的所属部分。而ISO C并不涉及文件描述符,所以仅有POSIX.1具有fdopen。
头文件: #include <stdio.h>
函数调用形式:fp = fopen("文件名","使用文件方式");
功能:以某种使用方式打开文件
返回值:若文件打开成功返回 一个有确定指向的FILE指针,打开失败则返回NULL。
使用方式含义
r 打开只读文件,该文件必须存在。
r+ 打开可读写的文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
rb 打开一个二进制文件,只允许读数据
rb+ 打开一个二进制文件,允许读和写
wb打开或建立一个二进制文件,只允许写数据
wb+ 打开或建立一个二进制文件,允许读和写
ab 打开一个二进制文件,并在文件末尾写数据
ab+ 打开一个二进制文件,允许读,或在文件末追加数据
总结以上使用方式的特点:
①r(read)代表读,w(write)代表写,a(append)追加。
②r为打开只读文件,即文件存在才能读;w为打开只写文件,若文件不存在则建立,文件存在则先将文件内容清空再写数据;a为追加的方式写入数据,即文件不存在则建立,若文件存在,原文件内容不清空,并在末尾写入数据。
③b代表二进制的标识,意思是对二进制文件操作
④+代表可读和可写
3.2 读操作
int fgetc(FILE *stream);
int getc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getchar(void);
char *gets(char *s);
fgetc()
读取文件指针stream所指向文件的下一个字符,返回值是所读取字符强制类型转换成整数的值,如果到达文件尾部或者出错,则返回EOF。
getc()与fgetc()
函数相同,只是它是一个宏实现。
getchar()
等同于getc(stdin)
从标准输入接收一个字符返回,多余的字符全部留在输入缓冲区,什么时候结束由程序员自己约定结束符,一般选择\n为结束符,当然也可以是任意其他字符。
gets()
从标准输入读取一行到字符串指针s所指向的缓冲区,直到行结束(‘\n’)或遇到一个EOF,但不接收’\n’,把 ‘\n’留存输入缓冲区;把接收的一串字符存储在形式参数指针指向的空间,并在最后自动添加一个’\0’**
注意:该函数不检查缓冲区是否够大,是否有溢出。**
fgets()从文件指针stream所指向的文件中,最多取出size个字符存放到s所指向的换中去中。遇到EOF或一行结束时,读取停止,把’\n’也作为一个字符接收;。如果读取一行,它将该行存放到缓冲区,在最后一个字符的后边添加’\0’并放到缓冲区。
返回值:
fgetc(), getc() 和getchar()成功时返回读取字符的ASCII码值,失败时返回EOF。
gets() 和fgets() 成功时返回字符串的指针s,失败时返回NULL指针。
scanf遇到空格、回车和Tab键都会认为输入结束,所有它不能接收空格。
3.3 写操作
int putc( int c,FILE *stream);
int fputc(int c,FILE *stream);
int putchar(int c);
int fputs(const char *str, FILE *stream);
int puts(const char *str);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
① putc 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
返回值:该函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
② fputc 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
返回值:如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
③ putchar把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。
返回值:该函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
f④ puts 把字符串写入到指定的流 stream 中,但不包括空字符。
返回值:该函数返回一个非负值,如果发生错误则返回 EOF。
puts 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中
参数:
fwrite 把 ptr 所指向的数组中的数据写入到给定流 stream 中。
ptr -- 这是指向要被写入的元素数组的指针。
size -- 这是要被写入的每个元素的大小,以字节为单位。
nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
返回值:
如果成功,该函数返回一个非负值,如果发生错误则返回 EOF。
3.4 随机存取
int fseek(FILE *stream, long int offset, int whence);
long int ftell(FILE *stream);
(1) fseek 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
参数:
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
offset -- 这是相对 whence 的偏移量,以字节为单位。
whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一
常量 描述
SEEK_SET 文件的开头
SEEK_CUR 文件指针的当前位置
SEEK_END 文件的末尾
返回值:如果成功,则该函数返回零,否则返回非零值。
(2) ftell 返回给定流 stream 的当前文件位置。
返回值:该函数返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。
demo:
#include <stdio.h>
int main ()
{
FILE *fp;
int len;
fp = fopen("file.txt", "r");
if( fp == NULL )
{
perror ("打开文件错误");
return(-1);
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fclose(fp);
printf("file.txt 的总大小 = %d 字节\n", len);
return(0);
}
3.5 关闭
int fclose(FILE *stream)
fclose 关闭流 stream。刷新所有的缓冲区。
返回值:如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
四、文件I/O操作的使用API总结
4.1 打开文件
4.2 读文件
4.3 写文件
4.4 随机存取
4.5 关闭文件
未完…