文件目录函数之二
时间:2007-05-11 来源:greentown
现在整理一下ASCII C支持的文件操作函数,他们是带有缓存的函数,这样做的原因是减少SYSTEM CALL的调用次数,也就是尽量减少调用write与read的函数,这里的缓分为三类,分别是全缓存,行缓存,无缓存,以下是摘自《UNIX高级环境编程》里面的对缓存的理解:
标准I/O提供了三种类型的缓存:
(1) 全缓存。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻在磁盘上的
文件通常是由标准I/O库实施全缓存的。在一个流上执行第一次I/O操作时,相关标准I/O函数通
常调用malloc获得需使用的缓存。
术语刷新(flush)说明标准I/O缓存的写操作。缓存可由标准I/O例程自动地刷新(例如当
填满一个缓存时),或者可以调用函数fflush刷新一个流。值得引起注意的是在U N I X环境中,
刷新有两种意思。在标准I/O库方面,刷新意味着将缓存中的内容写到磁盘上(该缓存可以只
是局部填写的)。在终端驱动程序方面(例如在第11章中所述的tcflush函数),刷新表示丢弃已
存在缓存中的数据。
(2) 行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I/O库执行I/O操作。这
允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I/O操
作。当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。
对于行缓存有两个限制。第一个是:因为标准I/O库用来收集每一行的缓存的长度是固定
的,所以只要填满了缓存,那么即使还没有写一个新行符,也进行I/O操作。第二个是:任何
时候只要通过标准输入输出库要求从(a)一个不带缓存的流,或者(b)一个行缓存的流(它预先
要求从内核得到数据)得到输入数据,那么就会造成刷新所有行缓存输出流。在(b)中带了一
个在括号中的说明的理由是,所需的数据可能已在该缓存中,它并不要求内核在需要该数据时
才进行该操作。很明显,从不带缓存的一个流中进行输入((a)项)要求当时从内核得到数据。
(3) 不带缓存。标准I/O库不对字符进行缓存。如果用标准I/O函数写若干字符到不带缓存
的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。标准出错流
stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新
行字符。
ANSI C要求下列缓存特征:
(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
(2) 标准出错决不会是全缓存的。
但是,这并没有告诉我们如果标准输入和输出涉及交互作用设备时,它们是不带缓存的还
是行缓存的,以及标准输出是不带缓存的,还是行缓存的。SVR 4和4.3+BSD的系统默认使用
下列类型的缓存:
• 标准出错是不带缓存的。
• 如若是涉及终端设备的其他流,则它们是行缓存的;否则是全缓存的。
对任何一个给定的流,如果我们并不喜欢这些系统默认,则可调用下列两个函数中的一个
更改缓存类型:
#include <stdio.h>
void setbuf(FILE* fp, char *buf) ;
int setvbuf(FILE* fp, char *buf, int mode, size_t size) ;
返回:若成功则为0,若出错则为非0 这些函数一定要在流已被打开后调用(这是十分明显的,因为每个函数都要求一个有效的文件 指针作为它们的第一个参数),而且也应在对该流执行任何一个其他操作之前调用。
可以使用setbuf函数打开或关闭缓存机制。为了带缓存进行I/O,参数buf 必须指向一个长
度为BUFSIZ的缓存(该常数定义在<stdio.h>中)。通常在此之后该流就是全缓存的,但是如果
该流与一个终端设备相关,那么某些系统也可将其设置为行缓存的。为了关闭缓存,将b u f设
置为NULL。
使用setvbuf,我们可以精确地说明所需的缓存类型。这是依靠mode参数实现的:
_IOFBF 全缓存
_IOLBF 行缓存
_IONBF 不带缓存
如果指定一个不带缓存的流,则忽略buf 和size 参数。如果指定全缓存或行缓存,则buf 和size
可以可选择地指定一个缓存及其长度。如果该流是带缓存的,而buf 是NULL,则标准I/O库将
自动地为该流分配适当长度的缓存。适当长度指的是由struct结构中的成员st_blksize所指定的
值(见4.2节)。如果系统不能为该流决定此值(例如若此流涉及一个设备或一个管道),则分
配长度为BUFSIZ的缓存。 他们大致可以分成五类来记。 一类为文件打开关闭函数: #include <stdio.h> FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fildes, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
int fclose(FILE *stream); 这一组函数中
第一个函数是直接打开一个给定路径的文件,第二个函数是打开一个指守文件描述符的文件,第三个函数的作用是在指定的流上打开指定的文件,如果该游戏已打开,则关闭该流再打开,linux里的man手册解释为: The primary use of the freopen function is to change the file associated with a standard text stream (stderr, stdin, or stdout). 《UNIX高级环境编程》里面的解释为: freopen在一个特定的流上(由fp指示)打开一个指定的文件(其路径名由pathname 指示),
如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:
标准输入、标准输出或标准出错。 很有意思的一个函数,给个例子看一下(代码文件名为:test.c): #include <stdio.h> int main(int argc, char** argv) {
FILE* pf = freopen("test.tmp", "w+", stdout);
if(pf){
printf("Have a look at the test.tmp file, what do you see?\n");
fclose(pf);
}
return 0;
}
这里要解释一下stdout,它与stdin,stderr都是申明在stdio.h里面的,表示标准输出,输入及错误游流。上一段代码是在标准输出流上打开一个文件test.tmp。并调用printf函数写字符串,我们知道,printf函数是向标准输出流中写的,如果在rh linux 9.0/kernel 2.4.2/gcc 3.2.2中输入命令
make test并执行生成的程序test,这时你不会在标准输出终端上,如显示器,看到任何输出,而cat test.tmp,你会看到printf写的字符串,很明显,freopen函数的很重要的一个作用就是把一个流,如标准流重新与某个文件关联起来。 这几个函数中的mode参数的意思为如何对打开的流进行操作,解释如下: r Open text file for reading. The stream is positioned at the
beginning of the file. r+ Open for reading and writing. The stream is positioned at the
beginning of the file. w Truncate file to zero length or create text file for writing.
The stream is positioned at the beginning of the file. w+ Open for reading and writing. The file is created if it does
not exist, otherwise it is truncated. The stream is positioned
at the beginning of the file. a Open for appending (writing at end of file). The file is cre-
ated if it does not exist. The stream is positioned at the end
of the file. a+ Open for reading and appending (writing at end of file). The
file is created if it does not exist. The stream is positioned
at the end of the file.
第二类函数是对字符操作的函数: #include <stdio.h> int getc(FILE *stream); int fgetc(FILE *stream); int getchar(void); 三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为EOF 其中getc是宏的展开,所以就不会有函数地址了,而fgetc是函数。 int putc(int c, FILE *stream); int fputs(const char *s, FILE *stream);
int putchar(int c);
三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为EOF 第三类函数是对字符串行进行操作: #include <stdio.h> char *gets(char *s); char *fgets(char *s, int n, FILE *stream);
两个函数返回:若成功则为b u f,若已处文件尾端或出错则为N U L L 这两个函数都指定了缓存地址,读入的行将送入其中。gets从标准输入读,而fgets则从指定的
流读。对于fgets,必须指定缓存的长度n。此函数一直读到下一个新行符为止,但是不超过n-1
个字符,读入的字符被送入缓存。该缓存以n u l l字符结尾。如若该行,包括最后一个新行符的
字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets的下一次 调用会继续读该行。gets是一个不推荐使用的函数。问题是调用者在使用gets时不能指定缓存的长 度。这样就可能造成缓存越界(如若该行长于缓存长度),写到缓存之后的存储空间中,从而产生不 可预料的后果。
int puts(const char *s); int fputs(const char *s, FILE *stream); 函数fputs将一个以null符终止的字符串写到指定的流,终止符null不写出。注意,这并不一定
是每次输出一行,因为它并不要求在n u l l符之前一定是新行符。通常,在null符之前是一个新
行符,但并不要求总是如此。puts将一个以null符终止的字符串写到标准输出,终止符不写出。但是, puts然后又将一个新行符写到标准输出。puts并不像它所对应的gets那样不安全。但是我们还是应避免使用它,以免需要记住它在最后又加上了一个新行符。如果总是使用fgets和fputs, 那么就会熟知在每行终止处我们必须自己加一个新行符。 第三类操作函数为二进制操作函数:
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); 下面我们给出一个例子说明这两个函数的作用: #include <stdio.h>
#include <stdlib.h>
#include <string.h> struct T
{
int a;
int b;
char buf[64];
}; int main(int argc, char** argv)
{
struct T t;
t.a = t.b = 0x64636261;
bzero(t.buf, 64 );
strncpy(t.buf, "I am the string data.", 64);
FILE* pf = fopen("test.tmp", "w+");
if(pf)
{
if(fwrite(&t, sizeof(struct T), 1, pf) != 1)
{
printf("Failed to write data\n");
exit(1);
}
fclose(pf);
} return 0;
} 这个程序是向test.tmp文件里写一个t结构的数据,而结构的数据为72bytes。生成并执行程序可以看到文件的大小正好为72个字节。 #include <stdio.h>
#include <stdlib.h>
#include <string.h> struct T
{
int a;
int b;
char buf[64];
}; int main(int argc, char** argv)
{
struct T t;
FILE* pf = fopen("test.tmp", "r");
if(pf)
{
if(fread(&t, sizeof(struct T), 1, pf) == 1)
{
printf("The string in strcut T: %s\n", t.buf);
}
fclose(pf);
} return 0;
}
这个程序是从test.tmp中读出一个T结构的数据,并输出里面的字符串,执行程序输出为: The string in strcut T: I am the string data.
还有一类函数是流定位的函数: #include <stdio.h>
long ftell(FILE* fp) ;
返回:若成功则为当前文件位置指示,若出错则为-1L
int fseek(FILE* fp,long offset, int whence) ;
返回:若成功则为0,若出错则为非0
void rewind(FILE* fp) 具体看参考linux里面的man手册。 最后一类是创建临时文件的函数: #include<stdio.h>
char *tmpnam(char* ptr) ;
返回:指向一唯一路径名的指针
FILE *tmpfile(void);
返回:若成功则为文件指针,若出错则为NULL 具体用法,可以参考相关文档及手册。
(1) 全缓存。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻在磁盘上的
文件通常是由标准I/O库实施全缓存的。在一个流上执行第一次I/O操作时,相关标准I/O函数通
常调用malloc获得需使用的缓存。
术语刷新(flush)说明标准I/O缓存的写操作。缓存可由标准I/O例程自动地刷新(例如当
填满一个缓存时),或者可以调用函数fflush刷新一个流。值得引起注意的是在U N I X环境中,
刷新有两种意思。在标准I/O库方面,刷新意味着将缓存中的内容写到磁盘上(该缓存可以只
是局部填写的)。在终端驱动程序方面(例如在第11章中所述的tcflush函数),刷新表示丢弃已
存在缓存中的数据。
(2) 行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I/O库执行I/O操作。这
允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I/O操
作。当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。
对于行缓存有两个限制。第一个是:因为标准I/O库用来收集每一行的缓存的长度是固定
的,所以只要填满了缓存,那么即使还没有写一个新行符,也进行I/O操作。第二个是:任何
时候只要通过标准输入输出库要求从(a)一个不带缓存的流,或者(b)一个行缓存的流(它预先
要求从内核得到数据)得到输入数据,那么就会造成刷新所有行缓存输出流。在(b)中带了一
个在括号中的说明的理由是,所需的数据可能已在该缓存中,它并不要求内核在需要该数据时
才进行该操作。很明显,从不带缓存的一个流中进行输入((a)项)要求当时从内核得到数据。
(3) 不带缓存。标准I/O库不对字符进行缓存。如果用标准I/O函数写若干字符到不带缓存
的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。标准出错流
stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新
行字符。
ANSI C要求下列缓存特征:
(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
(2) 标准出错决不会是全缓存的。
但是,这并没有告诉我们如果标准输入和输出涉及交互作用设备时,它们是不带缓存的还
是行缓存的,以及标准输出是不带缓存的,还是行缓存的。SVR 4和4.3+BSD的系统默认使用
下列类型的缓存:
• 标准出错是不带缓存的。
• 如若是涉及终端设备的其他流,则它们是行缓存的;否则是全缓存的。
对任何一个给定的流,如果我们并不喜欢这些系统默认,则可调用下列两个函数中的一个
更改缓存类型:
#include <stdio.h>
void setbuf(FILE* fp, char *buf) ;
int setvbuf(FILE* fp, char *buf, int mode, size_t size) ;
返回:若成功则为0,若出错则为非0 这些函数一定要在流已被打开后调用(这是十分明显的,因为每个函数都要求一个有效的文件 指针作为它们的第一个参数),而且也应在对该流执行任何一个其他操作之前调用。
可以使用setbuf函数打开或关闭缓存机制。为了带缓存进行I/O,参数buf 必须指向一个长
度为BUFSIZ的缓存(该常数定义在<stdio.h>中)。通常在此之后该流就是全缓存的,但是如果
该流与一个终端设备相关,那么某些系统也可将其设置为行缓存的。为了关闭缓存,将b u f设
置为NULL。
使用setvbuf,我们可以精确地说明所需的缓存类型。这是依靠mode参数实现的:
_IOFBF 全缓存
_IOLBF 行缓存
_IONBF 不带缓存
如果指定一个不带缓存的流,则忽略buf 和size 参数。如果指定全缓存或行缓存,则buf 和size
可以可选择地指定一个缓存及其长度。如果该流是带缓存的,而buf 是NULL,则标准I/O库将
自动地为该流分配适当长度的缓存。适当长度指的是由struct结构中的成员st_blksize所指定的
值(见4.2节)。如果系统不能为该流决定此值(例如若此流涉及一个设备或一个管道),则分
配长度为BUFSIZ的缓存。 他们大致可以分成五类来记。 一类为文件打开关闭函数: #include <stdio.h> FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fildes, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
int fclose(FILE *stream); 这一组函数中
第一个函数是直接打开一个给定路径的文件,第二个函数是打开一个指守文件描述符的文件,第三个函数的作用是在指定的流上打开指定的文件,如果该游戏已打开,则关闭该流再打开,linux里的man手册解释为: The primary use of the freopen function is to change the file associated with a standard text stream (stderr, stdin, or stdout). 《UNIX高级环境编程》里面的解释为: freopen在一个特定的流上(由fp指示)打开一个指定的文件(其路径名由pathname 指示),
如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:
标准输入、标准输出或标准出错。 很有意思的一个函数,给个例子看一下(代码文件名为:test.c): #include <stdio.h> int main(int argc, char** argv) {
FILE* pf = freopen("test.tmp", "w+", stdout);
if(pf){
printf("Have a look at the test.tmp file, what do you see?\n");
fclose(pf);
}
return 0;
}
这里要解释一下stdout,它与stdin,stderr都是申明在stdio.h里面的,表示标准输出,输入及错误游流。上一段代码是在标准输出流上打开一个文件test.tmp。并调用printf函数写字符串,我们知道,printf函数是向标准输出流中写的,如果在rh linux 9.0/kernel 2.4.2/gcc 3.2.2中输入命令
make test并执行生成的程序test,这时你不会在标准输出终端上,如显示器,看到任何输出,而cat test.tmp,你会看到printf写的字符串,很明显,freopen函数的很重要的一个作用就是把一个流,如标准流重新与某个文件关联起来。 这几个函数中的mode参数的意思为如何对打开的流进行操作,解释如下: r Open text file for reading. The stream is positioned at the
beginning of the file. r+ Open for reading and writing. The stream is positioned at the
beginning of the file. w Truncate file to zero length or create text file for writing.
The stream is positioned at the beginning of the file. w+ Open for reading and writing. The file is created if it does
not exist, otherwise it is truncated. The stream is positioned
at the beginning of the file. a Open for appending (writing at end of file). The file is cre-
ated if it does not exist. The stream is positioned at the end
of the file. a+ Open for reading and appending (writing at end of file). The
file is created if it does not exist. The stream is positioned
at the end of the file.
第二类函数是对字符操作的函数: #include <stdio.h> int getc(FILE *stream); int fgetc(FILE *stream); int getchar(void); 三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为EOF 其中getc是宏的展开,所以就不会有函数地址了,而fgetc是函数。 int putc(int c, FILE *stream); int fputs(const char *s, FILE *stream);
int putchar(int c);
三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为EOF 第三类函数是对字符串行进行操作: #include <stdio.h> char *gets(char *s); char *fgets(char *s, int n, FILE *stream);
两个函数返回:若成功则为b u f,若已处文件尾端或出错则为N U L L 这两个函数都指定了缓存地址,读入的行将送入其中。gets从标准输入读,而fgets则从指定的
流读。对于fgets,必须指定缓存的长度n。此函数一直读到下一个新行符为止,但是不超过n-1
个字符,读入的字符被送入缓存。该缓存以n u l l字符结尾。如若该行,包括最后一个新行符的
字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets的下一次 调用会继续读该行。gets是一个不推荐使用的函数。问题是调用者在使用gets时不能指定缓存的长 度。这样就可能造成缓存越界(如若该行长于缓存长度),写到缓存之后的存储空间中,从而产生不 可预料的后果。
int puts(const char *s); int fputs(const char *s, FILE *stream); 函数fputs将一个以null符终止的字符串写到指定的流,终止符null不写出。注意,这并不一定
是每次输出一行,因为它并不要求在n u l l符之前一定是新行符。通常,在null符之前是一个新
行符,但并不要求总是如此。puts将一个以null符终止的字符串写到标准输出,终止符不写出。但是, puts然后又将一个新行符写到标准输出。puts并不像它所对应的gets那样不安全。但是我们还是应避免使用它,以免需要记住它在最后又加上了一个新行符。如果总是使用fgets和fputs, 那么就会熟知在每行终止处我们必须自己加一个新行符。 第三类操作函数为二进制操作函数:
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); 下面我们给出一个例子说明这两个函数的作用: #include <stdio.h>
#include <stdlib.h>
#include <string.h> struct T
{
int a;
int b;
char buf[64];
}; int main(int argc, char** argv)
{
struct T t;
t.a = t.b = 0x64636261;
bzero(t.buf, 64 );
strncpy(t.buf, "I am the string data.", 64);
FILE* pf = fopen("test.tmp", "w+");
if(pf)
{
if(fwrite(&t, sizeof(struct T), 1, pf) != 1)
{
printf("Failed to write data\n");
exit(1);
}
fclose(pf);
} return 0;
} 这个程序是向test.tmp文件里写一个t结构的数据,而结构的数据为72bytes。生成并执行程序可以看到文件的大小正好为72个字节。 #include <stdio.h>
#include <stdlib.h>
#include <string.h> struct T
{
int a;
int b;
char buf[64];
}; int main(int argc, char** argv)
{
struct T t;
FILE* pf = fopen("test.tmp", "r");
if(pf)
{
if(fread(&t, sizeof(struct T), 1, pf) == 1)
{
printf("The string in strcut T: %s\n", t.buf);
}
fclose(pf);
} return 0;
}
这个程序是从test.tmp中读出一个T结构的数据,并输出里面的字符串,执行程序输出为: The string in strcut T: I am the string data.
还有一类函数是流定位的函数: #include <stdio.h>
long ftell(FILE* fp) ;
返回:若成功则为当前文件位置指示,若出错则为-1L
int fseek(FILE* fp,long offset, int whence) ;
返回:若成功则为0,若出错则为非0
void rewind(FILE* fp) 具体看参考linux里面的man手册。 最后一类是创建临时文件的函数: #include<stdio.h>
char *tmpnam(char* ptr) ;
返回:指向一唯一路径名的指针
FILE *tmpfile(void);
返回:若成功则为文件指针,若出错则为NULL 具体用法,可以参考相关文档及手册。
相关阅读 更多 +