免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1324 | 回复: 0
打印 上一主题 下一主题

GNULinux应用程序编程——第十章:在GNU/Linux当中的文件操作 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-29 23:31 |只看该作者 |倒序浏览
翻译自——GNULinux应用程序编程(GNU/Linux Application Programming) by M.Tim Jones
第十章:在GNU/Linux当中的文件操作
本章内容:
理解在GNU/Linux当中的文件操作APIs
探讨字符获取机制
探讨字符串获取机制
研究有序和无序(随机获取)方法
回顾文件获取的交替APIs和方法
简介
在这一章当中,我们将会学习GNU/Linux的文件操作APIs以及研究一些能够举例说明文件操作APIs的正确用法的应用程序。我们将会学习一些不同方式的文件操作函数,包括字符接口、字符串接口以及ASC||模式和二进制接口。本章将着重于APIs的讨论并通过它们在应用程序中的使用来举例说明它们的用法。
GNU/Linux中的文件操作
GNU/Linux中的文件操作是通过标准C库来完成的。我们可以通过使用相同的API函数来创建并操作ASC||文本文件或者二进制文件。我们可以对文件进行添加或者在文件当中进行查找。
在本章当中,我们将学习函数调用fopen(打开或者创建一个文件)、fwrite和fread(对文件进行读写操作)、fseek(在一个已经存在的文件当中定位于一个指定的位置)、feof(在读文件的时候测试文件指针是否处于文件的末尾)已经其它的低级别的调用(如:open、write以及read)。
文件操作API探秘
现在让我们亲自动手来完成GNU/Linux流文件I/O编程的一些例子。
创建一个文件句柄
要写一个执行文件操作的应用程序,我们首先必须使文件I/OAPIs可见。通过简单地包含头文件stdio.h我们就可以做到这一点,比如:
#include
不这么做的话将会产生编译错误(未经声明的符号)。第二步则是声明我们的将用于进行文件I/O操作中的句柄。这个句柄一般为一个文件指针,文件指针是一个不应该能够被开发者获取的透明的结构体。
FILE *my_fp;
在下面的部分当中我们将基于这一点来举例说明ASC||以及二进制应用程序。
打开一个文件
现在让我们来打开一个文件来举例说明不同的可用的模式。回顾我们前面所说的,打开一个文件的机制和创建一个文件的机制是一样的。我们将首先来研究这一点。
函数fopen是十分简单的,它提供了如下的API:
FILE *fopen( const chat *filename, const char *mode);
通过第一个参数filename我们指定了我们想要获取或者创建的文件名,然后通过mode指定了我们想要使用的模式。操作fopen产生的结果是一个FILE指针(它可以为NULL——意味着操作失败)。
操作fopen的关键点在于我们所提供的读写模式。表格10.1提供了一个初始化的获取模式的表单。
表格10.1:简单的文件读写模式
模式 描述
r 为读取文件打开一个已经存在的文件
w 为写文件打开一个文件(如果文件不存在就创建一个新的)
a 为添加文件而打开一个文件(如果文件不存在就创建一个新的)
aw 为读写文件而打开一个文件(如果文件不存在就创建一个新的)
读写模式仅仅是fopen函数用来决定怎么打开或者创建文件的一个字符串。如果我们想要创建一个新的文件,我们只需要简单地按如下方式来使用函数fopen:
my_fp = fopen( “myfile.txt”, “w”);
产生的结果将是一个为写操作做好了准备的新创建(同时也覆盖掉了原有的文件)的文件。相反,如果我们想要对一个业已存在的文件进行读操作,我们可以按如下方式来打开:
my_fp = fopen( “myfile.txt”, “r”);
注意:在这里我们只是简单地使用了一个不同的读写模式。读模式假定了文件已经存在,如果文件不存在的话,函数将会返回一个NULL。
在两种情况之下都是这么假定的:我们的文件myfile.txt不是业已在当前目录当中就是将会创建于当前目录之中。当前目录是我们调用我们所写的应用程序的所在之地。
检查I/O操作的结果是否成功是十分重要的。对于函数fopen来说,我们简单地测试了它的返回是否为NULL。对错误进行什么样的处理是由程序决定的(由你个人决定的)。清单10.1提供了一种错误处理的机制的例子:
清单10.1: 在一个fopen调用当中获取一个错误                           
     1:       #include
     2:       #include
     3:       #include
     4:   
     5:       #define MYFILE    "missing.txt"
     6:   
     7:       main()
     8:       {
     9:   
     10:         FILE *fin;
     11:   
     12:         /* Try to open the file for read */
     13:         fin = fopen( MYFILE, "r" );
     14:   
     15:         /* Check for failure to open */
     16:         if (fin == (FILE *)NULL) {
     17:   
     18:           /* Emit an error message and exit */
     19:           printf("%s: %s\n", MYFILE, strerror( errno ) );
     20:           exit(-1);
     21:   
     22:         }
     23:   
     24:         /* All was well, close the file */
     25:         fclose( fin );
     26:   
     27:       }
     28:                                                            
在清单10.1,我们使用一对新的没有介绍过的函数调用。在13行试图打开一个文件之后,我们通过检查来看一看新的文件句柄是否为NULL(0)。如果为NULL的话,我们就可以知道文件不存在或者我们不能够获取文件(我们没有采用合适的方式来获取文件)。在这样的情况之下,我们显示了一个由我们想要进行读操作而尝试打开的文件已经随后产生的错误消息组成的错误信息。我们通过变量errno捕获错误代码。变量errno是一个系统调用设置的暗示最后一个出现的错误的特殊变量。我们将这个值传递给一个能够将整数错误数字转换为一个适合在标准输出上打印的字符串的函数——strerror。执行我们的程序样例将会得到如下结果:
$ ./app
Missing.txt:  No such file or directory
$
现在让我们转到从一个文件当中读写数据上来。
读写数据
对一个文件进行读写有几种方法。拥有多个选择是很不错的,但是在什么样的对方使用什么样的机制也是十分重要的。例如,我们可以以字符为单位来进行读写,亦可以以字符串为单位来进行读写(只对于ASC||文本文件可行)。我们还可以使用一个更为普遍的方式(同时支持ASC||以及二进制文件)来允许读写数据。在这里我们将会学习每一种方法,但是我们的着重点将会落在后一个机制上。
标准输入输出库有一个缓冲接口。它有两个十分重要的特点。第一,系统以块为单位进行读写(块的大小一般为8KB)。字符I/O只是简单地被写到FILE缓冲器(当缓冲器满时,其内容将会自动地写到媒介上)上。第二,fflush是必要的,或者如果数据被发送到一个交互式设备(比如控制台终端)上时必须设置为无缓冲I/O。

字符接口
我们将于清单10.2和10.3中举例说明字符接口。在清单10.2当中,我们举例说明了如何使用fputc来进行字符输出。而在清单10.3当中,我们则介绍了如何使用fgetc来进行字符输入。这些函数拥有如下原型:
int  fputc( int c, FILE *stream);
int  fgetc( FILE * stream);
在这个例子当中我们产生了一个使用fputc的输出文件然后将这个文件用作fgetc的输入。在清单10.2当中,我们在11行打开我们的输出文件然后向文件输出我们的样例字符串。我们的简单的循环将会遍历整个字符串直到发现NULL,此时我们将退出以及关闭文件(21行)。在16行,我们使用fputc来显示我们的字符(作为一个int整数,根据fputc原型)同时指定我们的输出流(fout)。
清单10.2:   fpuc字符接口样例                                       
     1:       #include
     2:
     3:       int main()
     4:       {
     5:         int i;
     6:         FILE *fout;
     7:         const char string[]={"This\r\nis a test\r\nfile.\r\n\0"};
     8:   
     9:         fout = fopen("inpfile.txt", "w");
     10:   
     11:         if (fout == (FILE *)NULL) exit(-1);
     12:   
     13:         i = 0;
     14:         while (string != NULL) {
     15:   
     16:           fputc( (int)string, fout );
     17:           i++;
     18:      
     19:         }
     20:   
     21:         fclose( fout );
     22:   
     23:         return 0;
     24:       }
     25:                                                            
使用字符接口来读取这个文件的函数将会在清单10.3当中展示。这个函数和我们的文件创建的样例是十分地类似的。我们在第八行打开文件来进行读操作,在这之后的第十行有一个测试。然后我们进入一个循环从文件中来获取字符(12—22行)。循环只不过是使用了fgetc来从文件当中获取字符,然后当遇到特殊字符EOF的时候将会停止。EOF暗示了我们——这是文件的末尾了。对于所有的不是EOF的字符(16行),我们将会使用函数printf来显示字符到标准输出当中去。当文件内指针到了文件的末尾时,我们将会在24行使用fclose来关闭文件。
清单10.3:       fgetc字符接口样例                                    
     1:       #include
     2:   
     3:       int main()
     4:       {
     5:         int c;
     6:         FILE *fin;
     7:   
     8:         fin = fopen("inpfile.txt", "r");
     9:   
     10:         if (fin == (FILE *)0) exit(-1);
     11:   
     12:         do {
     13:   
     14:           c = fgetc( fin );
     15:   
     16:           if (c != EOF) {
     17:   
     18:             printf("%c", (char)c);
     19:   
     20:           }
     21:   
     22:         } while (c != EOF);
     23:   
     24:         fclose( fin );
     25:   
     26:         return 0;
     27:       }
     28:                                                            
执行了我们这个程序之后将会得到如下结果:
$ ./charout
$ ./charin
This
is a test
file.
$
很明显,字符接口十分地简单,同时它们的效率亦十分低。我们应该在应该基于字符串的方法不能够使用的时候才来使用它们。下一次我们再来看一看字符接口。
字符串接口
在这一部分当中,我们将来学习用于提供读写字符串的四个库函数。前两个(fputs和fgets)是简单的字符串接口,而后面两个(fprintf和fscanf)是更为复杂的提供了额外功能的字符串接口。
接口fputs和fgets借鉴了我们前面讨论的fputc以及fgetc这两个函数。它们提供了以应该十分简单的方式来进行读写字符串(variable-length)的方法。它们的原型如下:
int fputs( int c, FILE *stream );
char *fgets( char *s, int size, FILE *stream );
让我们首先来看一看一个从用户(从标准输入)接受字符串的样例程序(清单10.4),然后将它们写入一个文件。一旦程序接收了一个空行,我们将停止输入进程。
清单10.4: 将字符串写入文件                                            
     1:       #include
     2:   
     3:       #define LEN    80
     4:   
     5:       int main()
     6:       {
     7:         char line[LEN+1];
     8:         FILE *fout, *fin;
     9:   
     10:         fout = fopen( "testfile.txt", "w" );
     11:         if ( fout == (FILE *)0 ) exit(-1);
     12:   
     13:         fin = fdopen( 0, "r" );
     14:   
     15:         while ( (fgets( line, LEN, fin )) != NULL ) {
     16:   
     17:           fputs( line, fout );
     18:   
     19:         }
     20:   
     21:         fclose( fout );
     22:         fclose( fin );
     23:   
     24:         return 0;
     25:       }                                                      
在清单10.4当中的程序有一点小聪明。让我们一行一行地预排这个程序解释每一个要点。最奇怪的是我们在第七行声明了我们的字符串line(用来读取用户的输入)。然后我们声明了两个FILE指针,一个用于输入(fin),一个用于输出(fout)。
在第十行,我们使用函数fopen打开了我们的输出文件——一个新的文件(testfile.txt)。我们在第十一行检查了这一行的错误状态,如果出现错误而失败的话就退出。在第十三行,我们使用了一个特别的函数fdopen来关联一个存在的带有一个流的文件描述符。在这样的情况之下,我们将标准输入描述符in和带有一个新的流的fin(由fdopen返回)关联了起来。现在无论我们在标准输入当中输入了什么,我们的输入将会发送到这个文件流fin当中去。接下来,我们进入了一个尝试要从流fin(标准输入)当中读取并将其所读取写到输出流(fout)当中去的循环。在第十五行,我们使用fgets来进行读取并用NULL来检查fgets的返回。当我们关闭描述符(在键盘上按下Ctrl+D就可以搞定)的时候,NULL就会出现。然后提供使用fputs就可以将line读入的显示到输出流当中去。最后,当输入流被关闭时,我们退出我们的循环并且在第二十一行以及第二十二行关闭两个流。
现在让我们来看看另一个读取的样例,fgets。在这个样例当中(清单10.5),我们使用fgets读取了我们的test文件的内容,然后将它打印到本章输出当中去。
清单10.5: 从一个文件当中读取字符串             &n


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/6957/showart_349618.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP