Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4553
  • 博文数量: 3
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-20 20:39
文章分类
文章存档

2013年(3)

我的朋友

分类: IT职场

2013-04-13 19:52:39


    处理数据文件有时是比较困难的,本章将综合分析这方面的一些常见问题,例如流(stream)、文件模式(文本(text)和二进制(binary))以及文件和目录的处理等。目前,大多数专业程序是面向网络的,因此本章末尾讨论了有关文件共享和一致性控制的一些问题,希望读者认真阅读。此外,本章也讨论了许多与文件有关的问题,例如DOS中的文件句柄和硬件错误处理程序的安装。

   
    许多标准的C库函数都通过全局变量errno向程序传递一个错误号,以表明发生哪种错误,但是,你的程序不应该通过检查errno的值来判断是否发生了错误。通常,被调用的标准的C库函数都有一个返回值,该值将表示是否发生了错误,并且表示是否已给errno赋予了相应的错误号。在没有发生错误或所调用的函数不使用errno时,在errno中很可能仍然保留着一个错误号。有时,为了改善运行速度,使用errno的函数并不将errno清零。  
    总之,绝对不能单凭errno的值来判断是否发生了错误,而应该根据函数的返回值来判断是否应该检查errno的值。请参考你所使用的编译程序的有关文档,看看哪些函数使用了errno全局变量,以及errno的有效值清单。

   
    流是程序输入或输出的一个连续的字节序列,设备(例如鼠标、键盘、磁盘、屏幕、调制解调器和打印机)的输入和输出都是用流来处理的。在C语言中,所有的流均以文件的形式出现----不一定是物理磁盘文件,还可以是对应于某个输入/输出源的逻辑文件。C语言提供了5种标准的流,你的程序在任何时候都可以使用它们,并且不必打开或关闭它们。以下列出了这5种标准的流。
------------------------------------------------
    名称          描  述            例  子
------------------------------------------------
    stdin        标准输入           键盘
    stdout       标准输出            屏幕
    stderr       标准错误            屏幕
    stdprn       标准打印机          LPT1端口
    stdaux       标准串行设备        COM1端口
------------------------------------------------
    需要注意的是,stdprn和stdaux并不总是预先定义好的,因为LPT1和COM1端口在某些操作系统中是没有意义的,而stdin,stdout和stderr总是预先定义好的。此外,stdin并不一定来自键盘,stdout也并不一定显示在屏幕上,它们都可以重定向到磁盘文件或其它设备上。

    请参见:
    4.3 怎样重定向一个标准流?
    4.4 怎样恢复一个重定向了的标准流?
    4.5 stdout能被强制打印到非屏幕设备上吗?

   
    包括DOS在内的大多数操作系统,都提供了将程序的输入和输出重定向到不同设备上的手段。这就是说,程序的输出并不一定是到屏幕上,还可以重定向到文件或打印机端口上;程序的输入并不一定来自键盘,还可以重定向到文件上。
    在DOS中,重定向是通过重定向字符“<”和“>”来实现的。例如,如果你要求程序PRINTIT.EXE的输入来自文件STRINGS.TXT,你就可以在DOS提示符下键入如下命令:
    C:\>PRINTIT    请注意,可执行文件的名称总是第一个出现。“<”符号告诉DOS将STRINGS.TXT中的字符串作为程序PRINTIT.EXE的输入。关于重定向stdout标准流的例子请看4. 5。
    标准流的重定向并不一定总在操作系统下进行,在程序内部,用标准C库函数freopen()同样可以重定向标准流。例如,如果你要求在程序内部将标准流stdout重定向到文件OUTPUT.TXT,你就可以象下面这样使用freopen()函数:
    freopen("output.txt","w",stdout);
    现在,程序中每条输出语句(例如prinft();puts(),putch()等)输出的内容都将出现在文件OUTPUT.TXT中。

   请参见:
    4.2 什么是流(stream)?
    4.4 怎样恢复一个重定向了的标准流?
    4.5 stdout能被强制打印到非屏幕设备上吗?
  
   
    4.3中的例子演示了如何在程序内部重定向标准流。如果要将重定向了的标准流恢复到初始状态,可以使用标准C库函数dup()和fdopen()。
    dup()函数可以复制一个文件句柄,你可以用dup()函数保存对应于stdout标准流的文件句柄。fdopen()函数可以打开一个已用dup()函数复制了的流。这样,你就可以重定向并恢复标准流,请看下例:   

#include
void main(void);
void main(void)
{
    int orig_stdout;
    /* Duplicate the stdout file handle and store it in orig_stdout.  */
    orig_stdout = dup(fileno(stdout));
    /* This text appears on-screen.  */
    printf("Writing to original stdout... \n") ;
    /* Reopen stdout and redirect it to the "redir. txt" file.  */
    freopen("redir.txt", "w", stdout);
    /* This text appears in the "redir. txt" file.  */
    printf("Writing to redirected stdout.., \n");
    /* Close the redirected stdout.  */
    fclose (stdout);
    /* Restore the original stdout and print to the screen again.  */
    fdopen(orig_stdout, "w" );
    printf("I"m back writing to the original stdout. \n");
}

   4.2 什么是流(stream)?
    4.3 怎样重定向一个标准流?
    4. 5 stdout能被强制打印到非屏幕设备上吗?

   
    尽管标准流stdout的缺省方式是打印在屏幕上,但你可以将它重定向到其它设备上。请看下面的例子:
  /* redir.c */
  #include   
  void main(void);
  void main(void)
  {
      printf(”Let"s get redirectedI\n”),
  }
  在DOS提示符下,通过重定向字符“>”,可以将上例对应的可执行程序的输出重定向到非屏幕设备上。例如,下例将该程序的输出重定向到prn设备(通常就是连接到LPTl端口的打印机)上:   

C:\>REDIR>PRN

同样,你也可以将该程序的输出重定向到一个文件上,请看下例:
    C:\>REDIR>REDIR.OUT   
    在上例中,原来在屏幕上显示的输出内容将全部写入文件REDIR.OUT中。

    请参见:   
    4.2什么是流(stream)?
    4.3怎样重定向一个标准流?
    4.4怎样恢复一个重定向了的标准流?

   
    流可以分为两种类型:文本流和二进制流。文本流是解释性的,最长可达255个字符,其中回车/换行将被转换为换行符“\n”,反之亦然。二进制流是非解释性的,一次处理一个字符,并且不转换字符。   
    通常,文本流用来读写标准的文本文件,或者将字符输出到屏幕或打印机,或者接受键盘的输入;而二进制流用来读写二进制文件(例如图形或字处理文档),或者读取鼠标输入,或者读写调制解调器。   

    请参见:
    4.18怎样读写以逗号分界的文本?

   
  流函数(如fread()和fwrite())带缓冲区,在读写文本或二进制文件时效率更高。因此,一般来说,使用流函数比使用不带缓冲区的低级函数(如read()和write())会使程序性能更好。
    然而,在多用户环境中,文件需要共享,文件中的一部分会不断地被加锁、读、写或解锁,这时流函数的性能就不如低级函数好,因为共享文件的内容变化频繁,很难对它进行缓冲。因此,通常用带缓冲区的流函数存取非共享文件,用低级函数存取共享文件。

   
    C语言本身没有提供象dir_list()这样的函数来列出某个目录下所有的文件。不过,利用C语言的几个目录函数,你可以自己编写一个dir_list()函数。   
   首先,头文件dos.h定义了一个ffblk结构,它可以描述DOS下的文件信息,包括文件名、时间、日期、大小和属性。其次,C编译程序库中有findfirst()和findnext()这样两个函数,利用它们可以找到某个目录下符合查找要求的第一个或下一个文件。    
    findfirst()函数有三个参数,第一个参数指明要查找的文件名,例如你可以用“*.*”指明要查找某个目录下的所有文件。第二个参数指明要查找的文件属性,例如你可以指明只查找隐含文件或子目录。第三个参数是指向一个ffblk变量的指针,查找到的文件的有关信息将存放到该变量中。    
    findnext()函数在相应的目录中继续查找由findfirst()函数的第一个参数指明的文件。findnext()函数只有一个参数,它同样是指向一个ffblk变量的指针,查找到刚文件的有关信息同样将存放到该变量中。
    利用上述两个函数和ffblk结构,你就可以遍历磁盘上的某个目录,并列出该目录下所有的文件,请看下例:   

int findfirst(char *pathname,struct ffblk *ffblk,int attrib)查找指定的文件,成功返回0 pathname为指定的目录名和文件名,如"C:\\WPS\\TXT"
ffblk为指定的保存文件信息的一个结构,定义如下:
┏━━━━━━━━━━━━━━━━━━━┓
┃struct ffblk                        ┃
┃{                                   ┃
char ff_reserved[21]; /*DOS保留字*/┃
char ff_attrib; /*文件属性*/       ┃
┃ int ff_ftime; /*文件时间*/         ┃
┃ int ff_fdate; /*文件日期*/         ┃
┃ long ff_fsize; /*文件长度*/        ┃
char ff_name[13]; /*文件名*/       ┃
┃}                                   ┃
┗━━━━━━━━━━━━━━━━━━┛
attrib为文件属性,由以下字符代表
┏━━━━━━━━━┳━━━━━━━━┓
┃FA_RDONLY 只读文件┃FA_LABEL 卷标号┃
┃FA_HIDDEN 隐藏文件┃FA_DIREC 目录  ┃
┃FA_SYSTEM 系统文件┃FA_ARCH 档案   ┃
┗━━━━━━━━━┻━━━━━━━━┛


#include
#include
typedef struct ffblk FILE_BLOCK;
void main(void);
void main(void)
{
      FILE_BLOCK f_block;   /* Define the ffblk structure variable */
      int   ret_code;
      ret_code = findfirst("*.*",  &f_block,0);
      /* The findfirst() function returns a 0 when it is successful
         and has found a valid filename in the directory.  */
     printf("\nBegin scan... \n" );
     while (ret_code == 0)
     {
          /* Print the file"s name */
          printf(" %-12s\n", f_block.ff_name);
          /* Use the findnext() function to look
               for the next file in the directory.  */
          ret_code = findnext(&f_block);
     }
     printf("\nEnd of directory listing. \n" );
}

    请参见:
    4. 9  怎样列出一个文件的日期和时间?
    4. 10 怎样对某个目录下的文件名进行排序?
    4. 11 怎样判断一个文件的属性?

   
在findirst()和findfnext()函数所返回的ffblk结构中(请参见4.8),存放着查找到的文件的日期和时间,因此,只要对4.8中的例子稍作改动,就可以列出每个文件的日期、时间和文件名。
    文件的日期和时间存放在结构成员ffblk.ff_fdate和ffblk.ff_ftime中。文件的时间存放在一个双字节的无符号整数中,见下表:   
-------------------------------------------------------------   
  元  素    位域大小               取值范围
-------------------------------------------------------------
    秒        5位             0—29(乘以2后为秒值)
    分        6位             0—59
    时        5位             0—23
-------------------------------------------------------------
    文件的日期同样也存放在一个双字节的无符号整数中,见下表:
-------------------------------------------------------------
  元  素    位域大小             取值范围
-------------------------------------------------------------
    日        5位                1—31
    月        4位                1—12   
    年        7位                1--127(加上1980后为年值)
-------------------------------------------------------------
    因为DOS存储文件的秒数的间隔为两秒,所以只需使用0--29这个范围内的值。此外,DOS产生于1980年,因此文件的日期不可能早于1980年,你必须加上“1980”这个值才能得到真正的年值。
    以下是列出某个目录下所有的文件及其日期和时间的一个例子:

#include
#include

typedef struct ffblk FILE_BLOCK;
void main(void);
void main(void)
{
      FILE_BLOCK f_block;  /* Define the find-t structure variable */
      int ret_code;        /* Define a variable to store return codes */
      int hour;            /* We"re going to use a 12-hour clockl */
      char * am_pm;        /* Used to print "am" or "pm" */

      printf("\nDireetory listing of all files in this directory:\n\n");
      /* Use the "*.*" file mask and the 0xFF attribute mask to list
           all files in the directory, including system files, hidden
           files, and subdirectory names.  */

      ret_code = findfirst("D:\\turboc2\\test\\*.*", &f_block,0);
      /* The findfirst() function returns a 0 when it is successful
         and has found a valid filename in the directory.  */
     while (ret_code == 0)
     {
         /* Convert from a 24-hour format to a 12-hour format.  */
         hour = (f_block.ff_ftime>>11);
         if (hour > 12)
         {
              hour = hour - 12;
              am_pm = "pm";
         }
         else
              am_pm="am";
         /* Print the file"s name, date stamp, and time stamp.  */
         printf("%-12s %8s  %6s\n","filename","date" ,"time");
         printf("%-12s %02d/%02d/%4d  %02d:%02d:%02d %s\n",
                      f_block.ff_name,                     /* name */
                      (f_block.ff_fdate>> 5) & 0x0F,       /* month */
                      (f_block.ff_fdate) & 0x1F,           /* day   */
                      (f_block.ff_fdate>> 9) + 1980 ,      /* year */
                      hour,                                /* hour */
                      (f_block.ff_ftime >> 5) & 0x3F,      /* minute */
                      (f_block.ff_ftime & 0x1F) * 2,       /* seconds */
                      am_pm);
         /* Use the findnext() function to look
              for the next file in the directory.  */
 
         ret_code =  findnext (&f_block);
     }
     printf("\n End of directory listing. \n" );
}

   请注意,为了获得时间变量和日期变量的各个元素,要进行大量移位操作和位处理操作,如果你非常讨厌这些操作,你可以自己定义一个ffblk这样的结构,并为C语言定义的ffblk结构和你自己定义的结构创建一个共用体(请看下例),从而改进上例中的代码。   

/ * This is the ffblk structure as defined by ANSI C.  * /
struct ffblk
{
     char reserved[21];
     char attrib;
     unsigned wr_time;
    unsigned wr_date;
     long size;
     char name[13];
/ * This is a custom ffblk structure where we
     separate out the bits used for date and time.  * /
struet my_find_t
{
     char reserved[21];
     char attrib;
     unstgned seconds: 5;
     unsigned minutes: 6;
     unsigned hours: 5;
     unsigned day: 5;
     unstgned month: 4;
     unsigned year: 7;
     long size;
     char name[13];
}

/* Now, create a union between these two strucures
   so that we can more easily access the elements of
     wr_date and wr_time.  * /

union file_info
{
      struct ffblk ft;
      struct my_find_t mft;
}

    用上例中的自定义结构和共用体,你就可以象下例这样来抽取日期变量和时间变量的各个元素,而不必再进行移位操作和位处理操作了:

...
file_info my_file;
...
printf(" %-12s %2d/%2d/%4d %2d: %2d: %2d %s\n",
             my_file, mfr.name,               / * name       * /
             my-file, mfr.month,              / * moth       * /
             my_file, mfr.day,                / * day        * /
             (my-file. mft.year + 1980),      / * year       * /
             my-file, raft. hours,            / * hour       * /
             my- file. mfr. minutes,          / * minute     * /
             (my_file. mft. seconds * 2),     / * deconds    * /
             am_pm);

    请参见:
    4.8  怎样列出某个目录下的文件?
    4.10 怎样对某个目录下的文件名进行排序?
    4. 11 怎样判断一个文件的属性?

   
    在4.8的例子中,用findfirst()和findnext()函数遍历目录结构,每找到一个文件名,就把它打印在屏幕上,因此,文件名是逐个被找到并列出来的。
    当你对某个目录下的文件名进行排序时,这种逐个处理的方式是行不通的。你必须先将文件名存储起来,当所有的文件名都找到后,再对它们进行排序。为了完成这项任务,你可以建立一个指向ffblk结构的指针数组,这样,每找到一个文件名,就可以为相应的ffblk结构分配一块内存,将其存储起来。当所有的文件名都找到后,就可以用qsort()函数按文件名对所得到的ffblk结构数组进行排序了。
    qsort()函数是一个标准C库函数,它有4个参数:指向待排数组的指针,待排元素的数目,每个元素的大小,指向用来比较待排数组中两个元素的函数的指针。比较函数是你要提供的一个用户自定义函数,根据所比较的第一个元素是大于、小于或等于第二个元素,它将返回一个大于、小于或等于0的值。
    请看下例:
#include

#include
#include

typedef struct ffblk FILE_BLOCK ;
int sort_files(FILE_BLOCK ** , FILE_BLOCK **);
void main(void);
void main(void)
{
      FILE_BLOCK f_block;        /* Define the ffblk structure variable */
      int ret_code;              /* Define a variable to store the return codes */
      FILE_BLOCK ** file_list;   /* Used to sort the files */
      int file_count;            /* Used to count the flies */
      int x;                     /* Counter variable */
      file_count = -1;
      /* Allocate room to hold up to 512 directory entries.  */
      file_list = (FILE_BLOCK ** )malloc(sizeof(FILE_BLOCK * ) * 512);
      printf("\nDirectory listing of all files in this directory ; \n\n");
      /* Use the "*.*" file mask and the 0xFF attribute mask to list all files in the directory, including system files, hidden files, and subdirectory names.  */
      ret_code = findfirst("*.*", &f_block ,0);
      /* The findfirst() function returns a 0 when it is successful
          and has found a valid filename in the directory.  */
      while (ret_code == 0 && file_count < 512)
      {
           /* Add this filename to the file list */
           file_list[++file_count] =(FILE_BLOCK *)malloc(sizeof(FILE_BLOCK));
           *file_list[file_count] = f_block;
           /* Use the findnext() function to look for the next file in the directory.*/
           ret_code = findnext(&f_block);
      }
      /* Sort the files */
      qsort(file_list, file_count, sizeof(FILE_BLOCK *), sort_files);
      /*Now,iterate through the sorted array of filenames and print each entry.*/
      for(x=0; x       {
          printf(" %-12s\n", file_list[x]->ff_name);
      }
      printf("\nEnd of directory listing. \n" );
}
int sort_files(FILE_BLOCK* * a, FILE_BLOCK* * b)
{
      return (strcmp((*a)->ff_name, (*b)->ff_name));
}

    在上例中,由用户自定义的函数sort_files()来比较两个文件名,它的返回值实际就是标准C库函数strcmp()的返回值。只要相应地改变sort_files()函数的操作对象,上例就可按日期、时间或扩展名进行排序。

    请参见:
    4.8  怎样列出某个目录下的文件?
    4.9  怎样列出一个文件的日期和时间?
    4. 11 怎样判断一个文件的属性?

 
      
    文件属性存放在结构成员find_t.attrib中(请参见4. 8)。find_t.attrib是一个字符,每一种文件属性都由find_t.attrib中的一位来表示。以下列出了有效的DOS文件属性:
---------------------------------------------------------
    值           描述                     常量
---------------------------------------------------------
    00H          普通                     (无) 
    01H          只读                     FA_RDONLY   
    02H          隐含文件                 FA_HIDDEN
    04H          系统文件                 FA_SYSTEM
    08H          卷标                     FA_LABLE
    10H          子目录                   FA_DIREC
    20H          档案文件                 FA_ARCHIVE
---------------------------------------------------------
 
    只要根据上表检查find_t.attrib中已被置位的位,就可判断一个文件的属性。例如,对于一个只读隐含系统文件,find_t.attrib的第 1,2和3位是被置位的,对于一个普通文件,find_t.attrib中没有一位被置位。只要让nnd—t.attrib和相应的常量(见上表)进行一次按位与操作,就可判断find_t.attrib的某一位是否已被置位。
    利用上述技巧,就可以列出一个文件的属性了,请看下例:

#include
#include
#include
#inelude
#include
#include
typedef struct find_t FILE_BLOCK
void main(void);
void main(void)
{
      FILE-BLOCK f_block;  /* Define the find-t structure variable * /
      int ret_code;     /* Define a variable to store the return codes * /
      printf("\nDireetory listing of all files in this direetory:\n\n") ;
      / * Use the" *.*" file mask and the 0xFF attribute mask to list
           all files in the directory, including system fries, hidden
           files, and subdirectory names.  * /
      ret_code = _dos_findfirst(" * . * ", 0xFF, ad_block);
      / * The_dos_findfirst() function returns a 0 when
           it is successful and has found a valid filename
            in the directory.  * /
    while(ret_code == 0) .
    {
          ** Print the file"s name * /
         printf(" %-12s", f_block, name );
         / * Print the read-only attribute * /
         printf(" %s", (f_block. attrib & FA_RDONLY) ? "R": ".");
         / * Print the hidden attribute * /
         printf(" %s", (f_block.attrib & FA_HIDDEN) ? "H": ".");
         / * Print the system attribute * /
         printf(" %s", (f_block.attrib & FA_SYSTEM) ? "S": ".");
         / * Print the directory attribute * /
         printf(" %s", (f_block.attrib & FA_DIREC) ? "D": ".");
         / * Print the archive attribute */
         printf(" %s\n", (f_block.attrib & FA_ARCH) ? "A": ".");
         / * Use the _dos_findnext()function to look
               for the next file in the directory.  * /
         ret_code = _dos_findnext (&f_block);
    }
    printf("\nEnd of directory listing. \n");
}

    请参见:
    4.8  怎样列出某个目录下的文件?
    4.9  怎样列出一个文件的日期和时间?
    4.10 怎样对某个目录下的文件名进行排序?

       
    标准C库函数getenv()能够检索任何指定的环境变量。getenv()函数有一个参数,是一个指向包含待检环境变量的字符串的指针。当检索成功时, getenv()函数将返回一个指向检中字符串的指针,反之,它将返回一个NULL指针。
    以下是一个检索并在屏幕上打印PATH环境变量的例子:

#include
#include
void main(void);
void main(void)
{
      char * env_string;
      env_string = getenv("PATH");
      if (env_string == (char * ) NULL)
          printf("\nYou have no PATH !\n");
      else
          printf("\nYour PATH is: %s\n", env_string);
}

   
    利用标准C库函数中的低级文件函数sopen(),可以以共享模式打开一个文件。从DOS3. 0开始,当装入并运行程序SHARE.EXE后,就可以以共享模式打开文件了。共享模式允许多个程序同时使用同一个文件,在这种模式下,你可以允许其它程序修改你正在修改的一个文件,sopen()函数有4个参数:指向要打开的文件的文件名的指针,文件打开模式,文件共享模式,以及创建文件时的文件创建模式。sopen()函数的第二个参数通常称为“操作标志”参数,它可以有以下几种值:
--------------------------------------------------------------------------
    常  量                                    描    述
--------------------------------------------------------------------------
    O_APPEND               每次写都从文件的末尾开始   
    O_BINARY               以二进制模式打开文件
    O_CREAT                若文件不存在,则创建该文件
    O_EXCL                 若已使用O—CREAT标志而文件已存在,则返回一个错误
    O_RDONLY               以只读方式打开文件
    O_RDWR                 以读写方式打开文件
    O_TEXT                 以文本模式打开文件
    O_TRUNC                打开已存在的文件,在写文件时覆盖原来的内容
    O_WRONLY               以只写方式打开文件
--------------------------------------------------------------------------
   
    sopen()函数的第二个参数通常称为“共享标志”,它可以有以下几种值:
--------------------------------------------------------------------------
    常  量                                描  述
---------------------------------------------------------------
    SH_COMPAT                 其它程序不能存取该文件   
    SH_DENYRW                 其它程序不能读/写该文件
    SH_DENYWR                 其它程序不能写该文件
    SH_DENYRD                 其它程序不能读该文件
    SH_DENYNO                 任何程序都可读/写该文件
---------------------------------------------------------------
    如果sopen()函数执行成功,它将返回一个非负值,即所打开文件的句柄;反之则返回-1,
并置全局变量errno为下列值之一:   
---------------------------------------------------
    常  量                     描  述
---------------------------------------------------
    ENOENT                路径或文件没找到
    EMnLE                 文件句柄已用完
    EACCES                不允许存取该文件
    EINVACC               无效存取代码
---------------------------------------------------

    下面是以共享模式打开一个文件的例子:   
#include
#include
#include
#include
#include
void main(void) ;
void main(void)
{
      int file_handle;
      / * Note that sopen() is not ANSI compliant * /
      file_handle = sopen (" C : \\ DATA\\ TEST.DAT, O_RDWR, SH_DENYNO);
      close (file_handle);
}

 如果你和其它程序正在共享一个文件,那么当你正在修改该文件的某一部分时,应确保用
  标准C库函数locking()加锁该部分,详见4.15中对locking()函数的介绍。
   
    请参见:
    4.14 怎样确保只有你的程序能存取一个文件?
    4.15 怎样防止其它程序修改你正在修改的那部分文件内容?

   
    你可以用sopen()函数以共享模式打开一个文件,并用SH_DENYWR标志明确表示不允许其它程序读写该文件,请看下例:
    /* Notethatthe sopen()functionis not ANSIcompliant...*/
    fileHandle = sopen("C:\\DATA\\SETUP.DAT",O_RDWR,SH_DENYWR);
    在你的程序中加入上述语句后,将禁止其它程序存取SETUP.DAT文件;如果另一个程序试图打开该文件进行读或写,它将收到一个EACCES错误代码,说明禁止存取该文件。

    请参见:
    4.13 怎样打开一个同时能被其它程序修改的文件?
    4.15 怎样防止其它程序修改你正在修改的那部分文件内容?

    
    标准C库函数locking()可以用来加锁或解锁共享文件的一部分内容。
    locking()函数有3个参数:要加锁或解锁的共享文件的句柄,要对该文件进行的操作,以及要加锁或解锁的字节数。将被加锁或解锁的区域是从文件指针的当前位置开始的若干字节,因此,如果你不是从文件的头部开始加锁或解锁若干字节,就要用lseek()函数对文件指针进行重新定位。
    以下是对一个二进制文件SONGS.DAT进行加锁和解锁的例子:
#include
void main(void);
void main(void)
{
     int  file_handle, ret-coder;
     char * song_name = "Six Months In A Leaky Boat";
     char  rec_buffer[50];
     file_handle = sopen (" C: \\ DATA\\ SONGS.DAT", O_RDWR, SH_DENYNO);
      / * Assuming a record size of 50 bytes, position the file
           pointer to the 10th record.  * /
     lseek (file_handle, 450, SEEK_SET);
      /* Lock the 50-byte record.  * /
     ret_code = locking(file_handle, LK_LOCK, 50);
      / * Write the data and close the file.  * /
     memset (rec_buffer, "\0", sizeof (rec_buffer) );
     sprintf(rec_buffer, "%s", song_name);
     write (file handle, rec_buffer, sizeof (rec_buffer));
     lseek(file_handle, 450, SEEK_SET);
     locking(file_handle, LK_UNLCK, 50);
     close (file_handle);
}

   请注意,在上例中,在加锁第10个记录前,先用lseek()函数将文件指针移到了第10个记录(即第450个字节);同样,在解锁第10个记录前,也重新定位了文件指针。
 请参见:
    4.13怎样打开一个同时能被其它程序修改的文件?
    4.“怎样确保只有你的程序能存取某个文件?

   
    DOS的系统配置文件CONFIG.SYS通常包含一条“FILES=”语句,它告诉DOS应该分配多少个文件句柄供你的程序使用。在许多情况下,尤其是在使用Microsoft Windows或数据库程序时,20个文件句柄一般是不够用的,这时,你可以将"FILES=”语句中的文件句柄数目修改为你所需要的数目。如果 CONFIG.SYS文件中没有“FILES=”语句,你可以在它的末尾加入这条语句。例如,下述语句将分配100个文件句柄供系统使用:
  FILES = 100
    在大多数系统中,100个文件句柄足够用了。
    如果你碰巧遇到异常的程序冲突,那可能是因为你分配给系统的文件句柄太少,这时你可
  能想多分配一些文件句柄。不过,需要注意的是,每个文件句柄都将占用内存,因此分配大量的
  文件句柄是有代价的——你分配的文件句柄越多,系统用来运行程序的内存就越少。此外,文
  件句柄不仅分配给数据文件,有时还要分配给二进制文件,例如可执行程序。

   
    当DOS检测到一个严重错误时,它将调用中断24——严重错误处理中断,该中断24的缺省处理程序将显示"Abort,Retry,Fail”消息。    
    标准C库函数harderr()能接管对中断24的调用的处理。harderr()函数有一个参数,是指向一个用户编写的硬件错误处理函数的指针,利用这个硬件错误处理函数,你可以根据不同的硬件错误显示自己定义的消息,从而避开“Abort,Retry,Fail”消息。例如,当要求插盘而你没有插入盘时,你的程序就可以显示一条更明确的消息。
    当发生严重错误并调用了上述硬件错误处理函数后,该函数可调用C库函数hardretn()将控制权返回给你的应用程序,或者调用C库函数 hardresume()将控制权返回给DOS。通常,用hardresume()函数可以检测到磁盘错误,并位程序继续运行。其它设备错误(例如文件分配表损坏)有时是致命的,因此要用hardretn()函数来处理。


阅读(263) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~