标准I/O又称为带缓存的I/O,标准I/O库是由ANSI C标准进行规范和说明的,基本所有的操作系统上都支持此库。标准I/O库处理了很多细节,例如,缓存分配、优化长度执行I/O等。这样,用户不必担心如何选择使用正确的块长度。标准I/O库是在系统调用函数基础上构造的,它便于用户使用,但是如果不较深入地了解库的操作,也会带来一些问题。
1.流和FILE对象
缓冲型文件系统把每个设备都转换成一个逻辑设备,叫做流。所有的流具有相同的行为。流基本上与设备无关。有两种类型的流:文本流和二进制流。
所有的初级I/O函数都是针对文件描述符的。当打开一个文件时,即返回一个文件描述符,然后该文件描述符就用于后续的I/O操作。而对于标准I/O库,它们的操作则是围绕流(stream)进行的。
当用标准I/O库打开或创建一个文件时,会使一个流与一个文件相结合。当打开一个流时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际I/O的文件描述符、指向流缓存的指针、缓存的长度、当前在缓存中的字符数、出错标志等。
应用程序没有必要检验FILE对象。为了引用一个流,需将FILE指针作为参数传递给每个标准I/O函数。在本书中,我们称指向FILE对象的指针(类型为FILE *)为文件指针。
2.文件概述
所谓“文件”,是指一组相关数据的有序集合,这个数据集叫做文件,数据集的名字叫做文件名。实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。
文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中。从不同的角度可对文件进行不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。
普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序,也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、可执行程序可以称为程序文件,对输入输出数据可称为数据文件。
设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看做是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。
通常把显示器定义为标准输出文件,一般情况下,在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf函数就是这类输出。
通常把键盘定义为标准输入文件,从键盘上输入就意味着从标准输入文件上输入数据。scanf函数就属于这类输入。
从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。
例如,数5678的存储形式为:
ASCII码: 00110101 00110110 00110111 00111000
↓ ↓ ↓ ↓
十进制码: 5 6 7 8
共占用4个字节。
ASCII码文件可在屏幕上按字符显示。例如,源程序文件就是ASCII文件,由于是按字符显示,因此,人们能读懂其文件内容。
二进制文件是按二进制的编码方式来存放文件的。
例如, 数5678的存储形式为:
00010110 00101110
只占2个字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入/输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制,因此也把这种文件称为“流式文件”。
流式文件的操作主要有打开、关闭、读、写、 定位等各种操作。
1.1 文件打开方式
1.文件打开的方式
标准I/O库打开文件的方式共有12种,表10-1列出了文件的打开方式及其意义说明。
表10-1 文件打开方式表
文件打开方式 | 意义说明 |
rt | 只读打开一个文本文件,只允许读数据 |
wt | 只写打开或建立一个文本文件,只允许写数据 |
at | 追加打开一个文本文件,并在文件末尾写数据 |
rb | 只读打开一个二进制文件,只允许读数据 |
wb | 只写打开或建立一个二进制文件,只允许写数据 |
ab | 追加打开一个二进制文件,并在文件末尾写数据 |
rt+ | 读写打开一个文本文件,允许读和写 |
wt+ | 读写打开或建立一个文本文件,允许读写 |
at+ | 读写打开一个文本文件,允许读,或在文件末追加数据 |
rb+ | 读写打开一个二进制文件,允许读和写 |
wb+ | 读写打开或建立一个二进制文件,允许读和写 |
ab+ | 读写打开一个二进制文件,允许读,或在文件末追加数据 |
2.文件打开方式说明
对于文件的打开方式,有以下几点说明:
文件的打开方式由r、w、a、t、b、+共6个字符组成,各字符的含义如下:
① r(read) : 读。
② w(write) : 写。
③ a(append): 追加。
④ t(text) : 文本文件,可省略不写。
⑤ b(banary): 二进制文件。
⑥ +: 读和写。
凡用"r"打开一个文件时,该文件必须已经存在,且只能从该文件读出。
用"w"打开的文件只能向该文件写入。若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。
若要向一个已存在的文件追加新的信息,只能用"a"方式打开文件。但此时该文件必须是存在的,否则将会出错。
在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并做相应的处理。
把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码。因此,文本文件的读写要花费较多的转换时间,而对二进制文件的读写则不存在这种转换。
标准输入文件(键盘)、标准输出文件(显示器)、标准出错输出(显示器)是由系统打开的,可直接使用。
3.标准I/O文件函数分类说明
表10-2列出标准I/O文件函数的分类及其简要说明。
表10-2 标准I/O文件函数分类表
文件函数分类 | 函数名称 |
打开关闭文件函数 | fopen()和fclose() |
字符读写函数 | fgetc()和fputc() |
字符串读写函数 | fgets()和fputs() |
数据块读写函数 | fread()和fwrite() |
格式化读写函数 | fprintf()和fscanf() |
取得文件流的读取位置 | ftell() |
移动文件流的读写位置 | fseek() |
文件结束检测函数 | feof() |
1.2 标准I/O函数说明及程序范例
1. 打开关闭文件
(1) 函数原型
fopen(打开文件) |
所需头文件 | #include <stdio.h> |
函数说明 | 根据文件路径打开文件 |
函数原型 | FILE * fopen(const char * path, const char * mode) |
函数传入值 | path:打开的文件路径及文件名 |
mode:代表流的形态。参见文件处理方式 | |
函数返回值 | 成功:指向该流的文件指针就会被返回 |
出错:返回NULL,并把错误代码存在errno 中 |
fdopen(将文件描述符转为文件指针) |
所需头文件 | #include <stdio.h> |
函数说明 | fdopen()会将参数fildes的文件描述符转换为对应的文件指针后返回 |
函数原型 | FILE * fdopen(int fildes,const char * mode) |
函数传入值 | fildes:文件描述符 |
mode: 代表文件指针的流形态,此形态必须和原先文件描述符读写模式相同。mode方式参见文件处理方式 | |
函数返回值 | 成功:转换成功时返回指向该流的文件指针 |
错误:返回NULL,并把错误代码存在errno中 |
freopen(打开文件) |
所需头文件 | #include <stdio.h> |
函数说明 | 参数path字符串包含欲打开的文件路径及文件名,参数mode请参考fopen()说明。参数stream为已打开的文件指针。freopen()会将原stream所打开的文件流关闭,然后打开参数path的文件 |
函数原型 | FILE * freopen(const char * path,const char * mode,FILE * stream) |
函数传入值 | path:文件路径全称 |
mode: 参见文件处理方式 | |
stream:原先打开的文件流 | |
函数返回值 | 成功:文件顺利打开后,指向该流的文件指针就会被返回 |
错误:返回NULL,并把错误代码存在errno中 |
fclose(关闭文件) |
所需头文件 | #include <stdio.h> |
函数说明 | fclose()用来关闭先前fopen()打开的文件。此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源 |
函数原型 | int fclose(FILE * stream) |
函数传入值 | stream:文件指针 |
函数返回值 | 成功:0 |
出错:返回EOF,并把错误代码存到errno |
fileno(返回文件流所使用的文件描述符) |
所需头文件 | #include <stdio.h> |
函数说明 | fileno()用来取得参数stream指定的文件流所使用的文件描述符 |
函数原型 | int fileno(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 返回文件描述符 |
(2) 打开关闭函数举例
fopen.c源代码如下:
#include <stdio.h>
int main()
{
FILE * fp;
int fd ;
fp=fopen("noexist","a+");
if(fp==NULL)
{
perror("fopen error") ;
return;
}
fd = fileno( fp ) ;
printf("fildes no=%d\n", fd ) ;
fclose(fp);
return 0 ;
}
编译 gcc fopen.c -o fopen。
执行 ./fopen,执行结果如下:
fildes no=3
fdopen.c源代码如下:
#include <stdio.h>
int main()
{
FILE *fp =fdopen(0,"w+");
fprintf(fp,"%s\n","hello!");
fclose(fp);
return 0 ;
}
编译 gcc fdopen.c -o fdopen。
执行 ./fdopen,执行结果如下:
hello!
freopen.c源代码如下:
#include <stdio.h>
int main()
{
FILE * fp;
fp=fopen("/etc/passwd","r");
if ( NULL == fp )
{
perror("fopen error") ;
return -1;
}
printf("first fildes no=%d\n", fileno(fp)) ;
fp=freopen("/etc/group","r",fp);
if ( NULL == fp )
{
perror("fopen error") ;
return -1;
}
printf("second fildes no=%d\n", fileno(fp)) ;
fclose(fp);
return 0 ;
}
编译 gcc freopen.c -o freopen。
执行 ./freopen,执行结果如下:
first fildes no=3
second fildes no=3
可见freopen是先关闭文件描述符,然后重新打开文件描述符。
2. 以字符串为单位的I/O函数
(1) 函数原型
fgets(从文件中读取一字符串) |
所需头文件 | #include <stdio.h> |
函数说明 | 用来从参数stream所指的文件内读入字符并存到参数s所指的内存空间,直到出现换行字符、读到文件尾或是已读了size-1个字符为止,最后会加上NULL作为字符串结束 |
函数原型 | char * fgets(char * s,int size,FILE * stream) |
函数传入值 | s:读取内容存放字符串地址 |
size:所读的最大长度 | |
stream:文件指针 | |
函数返回值 | 成功:返回s的指针 |
出错:返回NULL |
fputs(将一指定的字符串写入文件内) |
所需头文件 | #include <stdio.h> |
函数说明 | 用来将参数s所指的字符串写入到参数stream所指的文件内 |
函数原型 | int fputs(const char * s,FILE * stream) |
函数传入值 | s:读取内容存放字符串地址 |
stream:文件指针 | |
函数返回值 | 成功:返回写出的字符个数 |
出错:返回EOF则表示有错误发生 |
(2) 函数举例
fgets.c源代码如下:
#include <stdio.h>
int main()
{
char s[80];
fputs(fgets(s,80,stdin),stdout);
return 0 ;
}
编译 gcc fgets.c -o fgets。
执行 ./fgets,执行结果如下:
this is a test /*输入*/
this is a test /*输出*/
3. 以块为单位的I/O函数
(1) 函数原型
fread(从文件流读取数据) |
所需头文件 | #include <stdio.h> |
函数说明 | 用来从文件流中读取数据,读取的字符数由参数size*nmemb来决定。fread()会返回实际读取到的nmemb数目,如果此值比参数nmemb小,则代表可能读到了文件尾或有错误发生,这时必须用feof()或ferror()来决定发生什么情况 |
函数原型 | size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream) |
函数传入值 | ptr:指向欲存放读取进来的数据空间 |
size:读取单个块长度 | |
nmemb:读取的块数 | |
stream: 已打开的文件指针 | |
函数返回值 | 实际读取到的nmemb数目 |
fwrite(将数据写至文件流) |
所需头文件 | #include <stdio.h> |
函数说明 | 用来将数据写入文件流中。总共写入的字符数由参数size*nmemb来决定。fwrite()会返回实际写入的nmemb数目 |
函数原型 | size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream) |
函数传入值 | ptr:指向欲写入的数据地址 |
size:写入单个块长度 | |
nmemb:写入的块数 | |
stream:已打开的文件指针 | |
函数返回值 | 实际写入的nmemb数目 |
(2) 函数举例
fwrite.c源代码如下:
#include <stdio.h>
struct record {
char name[10];
int age;
};
int main(void)
{
struct record array[2] = {{"Ken", 24}, {"Knuth", 28}};
FILE *fp = fopen("recfile", "w");
if (fp == NULL) {
perror("Open file recfile");
return -1;
}
/*变量在内存中为对齐存放,整型占4个字节,所以整型变量要存放在能除断4的内存地址上*/
printf("record length=%d\n", sizeof( struct record )) ;
printf("record name =%d, address=%d\n", sizeof(array[0].name), &array[0].name) ;
printf("record age=%d, address=%d\n ", sizeof(array[0].age), &array[0].age) ;
fwrite(array, sizeof(struct record), 2, fp);
fclose(fp);
return 0;
}
编译 gcc fwrite.c -o fwrite。
执行 ./fwrite,执行结果如下:
record length=16
record name =10, address=-1081549780
record age=4, address=-1081549768
fread.c源代码如下:
#include <stdio.h>
struct record {
char name[10];
int age;
};
int main(void)
{
struct record array[2];
FILE *fp = fopen("recfile", "r");
if (fp == NULL) {
perror("Open file recfile");
return -1;
}
fread(array, sizeof(struct record), 2, fp);
printf("Name1: %s\tAge1: %d\n", array[0].name, array[0].age);
printf("Name2: %s\tAge2: %d\n", array[1].name, array[1].age);
fclose(fp);
return 0;
}
编译 gcc fread.c -o fread。
执行 ./fread,执行结果如下:
Name1: Ken Age1: 24
Name2: Knuth Age2: 28
4. 以字节为单位的I/O函数
(1) 函数原型
fputc(将一指定字符写入文件流中) |
所需头文件 | #include <stdio.h> |
函数说明 | 将参数c 转为unsigned char 后写入参数stream 指定的文件中 |
函数原型 | int fputc(int c, FILE * stream) |
函数传入值 | c:写入成功的字符 |
stream:已打开的文件指针 | |
函数返回值 | 成功:写入的字符,即参数c |
错误:返回EOF(-1)则代表写入失败 | |
附加说明 | putc原型同fputc,其作用也相同,但putc()为宏定义,非真正的函数调用 |
fgetc(将从文件中读取一个字符) |
所需头文件 | #include <stdio.h> |
函数说明 | 从参数stream所指的文件中读取一个字符。若读到文件尾无数据时便返回EOF |
函数原型 | int fputc(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 成功:返回读到的字符 |
错误:返回EOF(-1)则代表读取失败 | |
附加说明 | getc原型同fgetc,其作用也相同,但getc()为宏定义,非真正的函数调用 |
getchar(从标准输入设备内读取一个字符) |
所需头文件 | #include <stdio.h> |
函数说明 | getchar()用来从标准输入设备中读取一个字符。然后将该字符从unsigned char转换成int后返回 |
函数原型 | int getchar(void) |
函数返回值 | getchar()会返回读取到的字符,若返回EOF,则表示有错误发生 |
putchar(将指定的字符写到标准输出设备) |
所需头文件 | #include <stdio.h> |
函数说明 | putchar()用来将参数c字符写到标准输出设备 |
函数原型 | int putchar (int c) |
函数返回值 | putchar()会返回输出成功的字符,即参数c。若返回EOF,则代表输出失败 |
附加说明 | putchar()函数是通过putc(c,stdout)的宏定义实现的 |
(2) 函数举例
fgetc.c源代码如下:
#include <stdio.h>
int main()
{
FILE *fp;
FILE *fp1;
int c;
fp=fopen("exist.txt","r");
if ( NULL == fp )
{
perror("fopen error") ;
return -1;
}
fp1= fopen("noexist.txt","w");
if ( NULL == fp1 )
{
perror("fopen error") ;
return -1;
}
while((c=fgetc(fp))!=EOF)
{
fputc(c, fp1) ;
}
fclose(fp);
fclose(fp1);
return 0 ;
}
编译 gcc fgetc.c -o fgetc。
创建exist.txt文件并输入内容,执行./fgetc。查看结果发现产生了noexist.txt,且两文件内容一样。
5. 操作读写位置的函数
(1) 函数原型
fseek(移动文件流的读写位置) |
所需头文件 | #include <stdio.h> | ||||||||||||||
函数说明 | 用来移动文件流的读写位置。参数offset为根据参数whence来移动读写位置的位移数 | ||||||||||||||
函数原型 | int fseek(FILE * stream,long offset,int whence) | ||||||||||||||
函数传入值 | stream:已打开的文件指针 | ||||||||||||||
offset:根据参数whence来移动读写位置的位移数,可为正、负、0 | |||||||||||||||
| |||||||||||||||
函数返回值 | 成功:0 | ||||||||||||||
错误:-1,errno会存放错误代码 | |||||||||||||||
常见使用方法 | ① 欲将读写位置移动到文件开头时用fseek(FILE *stream,0,SEEK_SET) ② 欲将读写位置移动到文件尾时用fseek(FILE *stream,0,0SEEK_END) |
rewind(重设文件流的读写位置为文件开头) |
所需头文件 | #include <stdio.h> |
函数说明 | rewind()用来把文件流的读写位置移至文件开头。参数stream为已打开的文件指针,此函数相当于调用fseek(stream,0,SEEK_SET) |
函数原型 | void rewind(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
ftell(取得文件流的读取位置) |
所需头文件 | #include <stdio.h> |
函数说明 | ftell()用来取得文件流目前的读写位置 |
函数原型 | long ftell(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 成功:目前的读写位置 |
错误:-1,errno会存放错误代码 | |
错误代码 | EBADF:参数stream无效或文件流的读写位置无效 |
feof(检查文件流是否读到了文件尾) |
所需头文件 | #include <stdio.h> |
函数说明 | 用来侦测是否读取到了文件尾。如果已到文件尾,则返回非零值,其他情况返回0 |
函数原型 | int feof(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 返回非零值代表已到达文件尾 |
(2) 函数举例
fseek.c源代码如下:
#include <stdio.h>
int main()
{
FILE * stream;
long offset;
stream=fopen("/etc/passwd","r");
fseek(stream,5,SEEK_SET);
printf("offset=%d\n",ftell(stream));
rewind(stream);
printf("offset = %d\n",ftell(stream));
printf("file eof status=%d\n", feof(stream)) ;
fseek(stream,0,SEEK_END);
printf("offset = %d\n",ftell(stream));
printf("eof=%d\n", fgetc(stream)) ;
printf("file eof status=%d\n", feof(stream)) ;
fclose(stream);
return 0 ;
}
编译 gcc fseek.c -o fseek。
执行 ./fseek,结果如下:
offset=5
offset = 0
file eof status=0
offset = 2676
eof=-1
file eof status=1
ftell.c源代码如下:
#include <stdio.h>
int main()
{
FILE *fp;
long flen;
char data[10000] ;
if( ( fp = fopen( "/etc/passwd" , "r" ) ) == NULL ){
perror("fopen error") ;
return -1 ;
}
fseek(fp,0L,SEEK_END); /* 定位到文件末尾 */
flen=ftell(fp); /* 得到文件大小 */
fseek(fp,0L,SEEK_SET); /* 定位到文件末尾 */
fread(data,flen,1,fp); /* 一次性读取全部文件的内容 */
fclose(fp);
printf("passwd file content:\n%s\n", data ) ;
return 0 ;
}
编译 gcc ftell.c -o ftell。
执行 ./ftell,执行结果如下:
passwd file content:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
......
6. 格式化读写函数
(1) 函数原型
fprintf(格式化输出数据至文件) |
所需头文件 | #include <stdio.h> |
函数说明 | fprintf()会根据参数format字符串来转换并格式化数据,然后将结果输出到参数stream指定的文件中,直到出现字符串结束('\0')为止 |
函数原型 | int fprintf(FILE * stream, const char * format, ...) |
函数传入值 | stream:文件描述符 |
format:格式项 | |
函数返回值 | 成功:返回实际输出的字节数 |
失败:-1,并把错误代码存在于errno中 |
fscanf(格式化字符串输入) |
所需头文件 | #include <stdio.h> |
函数说明 | fscanf()会从参数stream的文件流中读取字符串,再根据参数format字符串来转换并格式化数据。格式化转换形式请参考scanf()。转换后的结构存于对应的参数内 |
函数原型 | int fscanf(FILE * stream, const char *format, ...) |
函数传入值 | stream:文件描述符 |
format:格式项 | |
函数返回值 | 成功:返回参数数目 |
失败:-1,并把错误代码存在于errno中 |
(2) 函数举例
fprintf.c源代码如下:
#include <stdio.h>
int main()
{
int i = 150;
int j = -100;
double k = 3.14159;
int fp ;
fp = fprintf(stdout,"%d %f %x \n",j,k,i);
fp = fprintf(stdout,"%2d %*d\n",i,2,i);
printf(" fp=%d\n", fp) ;
return 0 ;
}
编译 gcc fprintf.c -o fprintf。
执行 ./fprintf,执行结果如下:
-100 3.141590 96
150 150
fp=8
fscanf.c源代码如下:
#include<stdio.h>
int main()
{
int i;
unsigned int j;
char s[5];
int fd ;
fd =fscanf(stdin,"%d %x %5[a-z] %*s %f",&i,&j,s,s);
printf("%d %d %s \n",i,j,s);
printf("fd=%d\n", fd) ;
return 0 ;
}
编译 gcc fscanf.c -o fscanf。
执行 ./fscanf,执行结果如下:
10 0x1b aaaaaaaaa bbbbbbbbbb /*从键盘输入*/
10 27 aaaaa
fd=3
7. 产生临时文件
mktemp函数只产生唯一的临时文件名,并不产生临时文件,而且存在安全隐患,不建议使用。mkstemp函数会产生唯一的临时文件。
(1) 函数原型
mktemp(产生唯一的临时文件名) |
所需头文件 | #include <stdlib.h> |
函数说明 | mktemp()用来产生唯一的临时文件名,并不创建文件。参数template所指的文件名称字符串中最后六个字符必须是XXXXXX,产生后的文件名会借字符串指针返回 |
函数原型 | char * mktemp(char * template) |
函数传入值 | template:所指的文件名称字符串,最后六个字符必须是XXXXXX |
函数返回值 | 成功:以指针方式返回产生的文件名 |
失败:NULL,并把错误代码存于errno中 | |
附加说明 | 参数template所指的文件名称字符串必须声明为数组,如: char template[ ]= "template-XXXXXX"; 不可用char * template="template-XXXXXX"; |
mkstemp(建立唯一的临时文件) |
所需头文件 | #include <stdlib.h> |
函数说明 | mkstemp()用来建立唯一的临时文件。参数template 所指的文件名称字符串中最后六个字符必须是XXXXXX。mkstemp()会以可读写模式来打开该文件,如果该文件不存在则会建立该文件。 |
函数原型 | int mkstemp(char * template) |
函数传入值 | template:所指的文件名称字符串,最后六个字符必须是XXXXXX |
函数返回值 | 成功:文件顺利打开后,返回该文件的文件描述符 |
失败:如果文件打开失败,则返回0,并把错误代码存于errno中 | |
附加说明 | 参数template所指的文件名称字符串必须声明为数组,如: char template[ ]= "template-XXXXXX"; 不可用char * template="template-XXXXXX"; |
(2) 函数举例
mktemp.c源代码如下:
#include <stdio.h>
int main()
{
char template[ ]="template-XXXXXX";
char *file ;
file = mktemp(template);
printf("template=%s\n",template);
printf("file=%s\n", file);
return 0 ;
}
编译 gcc mktemp.c -o mktemp。
执行 ./mktemp,执行结果如下:
template=template-oh2jT8
file=template-oh2jT8
mkstemp.c源代码如下:
#include <stdio.h>
int main()
{
int fd;
char template[ ]="template-XXXXXX";
fd=mkstemp(template);
printf("template = %s\n",template);
printf("fd = %d\n", fd);
close(fd);
return 0 ;
}
编译 gcc mkstemp.c -o mkstemp。
执行 ./mkstemp,执行结果如下:
template = template-rPGs2W
fd = 3
8. 错误检测与清除
(1) 函数原型
ferror(检查文件流是否有错误发生) |
所需头文件 | #include <stdio.h> |
函数说明 | ferror()用来检查参数stream所指定的文件流是否发生了错误情况,如有错误发生则返回非0值 |
函数原型 | int ferror(FILE *stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 如果文件流有错误发生则返回非0值 |
clearerr(清除文件流的错误标记) |
所需头文件 | #include <stdio.h> |
函数说明 | clearerr()清除参数stream指定的文件流所使用的错误标记 |
函数原型 | void clearerr(FILE * stream) |
函数传入值 | stream:已打开的文件指针 |
函数返回值 | 无 |
9. 文件编程综合实例
源代码file.c如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*去掉字符串左边指定字符*/
char *LTrimChar( char *pBuf, char cDel )
{
char *pTmp = pBuf ;
if ( ( cDel != '\0' ) && ( pTmp != NULL ) )
{
while ( *pTmp++ == cDel ) ;
strcpy( pBuf, pTmp - 1 );
}
return pBuf ;
}
/*去掉字符串右边指定字符*/
char *RTrimChar( char *pBuf, char cDel )
{
char *pTmp = pBuf;
if ( ( cDel != '\0' ) && ( pTmp != NULL ) )
{
for ( pTmp = pBuf + strlen( pBuf ) -1 ;
( *pTmp == cDel ) && ( pTmp >= pBuf ) ; *pTmp-- = '\0' );
}
return pBuf ;
}
/*去掉字符串两边指定字符*/
char *AllTrimChar( char *pBuf, char cDel )
{
return LTrimChar( RTrimChar( pBuf, cDel ) , cDel );
}
int main(int argc, char **argv)
{
char buff_read[128];
long line_length ;
char seri_no[8+1];
char cust_acct_no[22+1] ;
char amt[17+1] ; /*原文件金额除以100进行转换*/
char *ptr;
FILE *fpr; /*读文件指针*/
FILE *fpw; /*写文件指针*/
char file_read[64];
char file_write[64];
sprintf( file_read,"%s", "read.txt" );
if( ( fpr = fopen( file_read,"r" ) ) == NULL ) {
perror("fopen error\n");
return -1;
}
strcpy( file_write, "write.txt") ;
fpw = fopen( file_write , "w" );
if( fpw == NULL ) {
perror("fopen error\n");
return -1;
}
memset( buff_read , 0x00 , sizeof( buff_read ));
line_length= sizeof(buff_read) ;
while( fgets( buff_read, line_length, fpr ) != NULL ) {
memset(seri_no, 0x00, sizeof(seri_no)) ;
memset(cust_acct_no, 0x00, sizeof(cust_acct_no)) ;
memset(amt, 0x00, sizeof(amt)) ;
memcpy( seri_no , buff_read+0 , 8 );
AllTrimChar( seri_no, ' ' ) ; /*去掉两边的空格*/
memcpy( cust_acct_no , buff_read+8 , 22 );
AllTrimChar( cust_acct_no, ' ' ) ;
memcpy( amt , buff_read+30 , 17 );
AllTrimChar( amt, ' ' ) ;
fprintf(fpw,"%-8.8s%-22.22s%-17.2lf\n", seri_no,cust_acct_no, atof(amt)/100 ) ;
}
fclose( fpr );
fclose( fpw );
return 0 ;
}
编译 gcc file.c –o file。
read.txt文件内容为:
123456 12345678909876543210 1234567
123457 12345678909876543230 1234565
执行 ./file。发现建立了write.txt文件,其文件内容为:
123456 12345678909876543210 12345.67
123457 12345678909876543230 12345.65
摘录自《深入浅出Linux工具与编程》