c#发展

注册

 

发新话题 回复该主题

C语言输入输出流5freopen函数 [复制链接]

1#
专治白癜风的专科医院 https://disease.39.net/bjzkbdfyy/231013/j1hvyqj.html

前两篇文章介绍了fopen函数以及文件访问模式”w”、”r”、”a”、“+”、“t”、“b”的各种用法。本篇介绍fopen函数的安全版本fopen_s函数,以及数据流重定向函数freopen和它的安全版本freopen_s以,以及标准流被重定向后如何恢复等知识点。

fopen_s函数

这个函数是fopen函数的安全版本,它提供了比fopen函数更好的错误检查机制,以防止缓冲区溢出等问题。

函数原型

errno_tfopen_s(

FILE**pFile,

constchar*filename,

constchar*mode

);

参数解释:

pFile

类型:FILE**(指向FILE指针的指针)

含义和用法:这是一个指向FILE指针的指针,用于存储成功打开文件后返回的文件指针。如果函数成功打开文件,它会在pFile所指向的位置存储一个有效的FILE指针。如果函数失败,FILE*对象将是NULL。

举例:

FILE*fp;

fopen_s(fp,”foo.txt”,”r”);

//假设foo.txt不存在,

//fp的值就是NULL,

//因为r模式要求文件必须存在。

filename

类型:constchar*

含义和用法:这是要打开文件的名称(包括路径)。它应该是一个以空字符(\0)结尾的字符串。

mode

类型:constchar*

含义和用法:这是文件的打开模式。它决定了文件应如何被打开和访问。例如,r表示以只读方式打开文件,w表示以写入方式打开文件(如果文件已存在,则其内容将被覆盖),a表示以追加模式打开文件等。还可以组合使用其他字符以提供额外的选项,例如b用于二进制模式。详细内容可以参看之前的文章:《C语言输入输出流()文件打开函数和文件访问模式》

返回值

类型:errno_t

作用:如果函数成功打开文件,则返回零。如果函数失败,则返回非零错误代码。比如用r模式打开一个不存在的文件,就会返回非零错误码为2,也可以用perror函数输出错误码代表的具体含义。

举例:

FILE*fp;

errno_terr;

err=fopen_s(fp,”foo.txt”,”r”);

//假设foo.txt不存在

if(err!=0){

printf(“errorcode:%d.\n”,err);

//此时printf输出错误码2

perror(“errormessage:”);

//此时perror输出错误码的含义:Nosuchfileordirectory

下面是一个完整的例子:

#includestdio.h

#includeerrno.h

intmain(){

  FILE*fp;

  errno_terr;

  err=fopen_s(fp,"foo.txt","r");

  if(err==0)

    printf("文件被正常打开。\n");

  else{

    printf("fpisNULL.\n");

    printf("错误码:%d.\n",err);

    perror("错误码含义:");

    returnerr;

  }

  fclose(fp);

  return0;

}

程序运行效果如下图:

图1完整的错误码数字和含义请参见这篇文章:《C语言错误码:perror函数和errno_t数据类型》freopen函数

freopen函数的作用非常强大,拥有比fopen更灵活的用途。它的主要作用归纳如下:

将标准流重定向到文件流

将文件对象重定向到另一个文件

将文件流重新打开改变访问模式

如果不明白也没关系,下面我们来详细的介绍下函数的用法。

函数原型如下:

FILE*freopen(

constchar*filename,

constchar*mode,

FILE*stream);

参数:

filename

将要重新定向到的文件名称。这个参数是一个指向字符的指针,通常是一个字符串常量。

mode

文件的访问模式。

stream

要被重新改变指向的标准流或者文件流。这通常是一个指向FILE类型的指针,代表一个已经打开的文件流,比如stdin(标准输入流)、stdout(标准输出流)或stderr(标准错误流)。

返回值

FILE*

freopen函数的返回值是一个指向新文件流的指针。如果文件顺利打开,它将返回这个指针;如果文件打开失败,它将返回NULL,并将错误代码存储在全局变量errno中,用perror函数可以输出。错误代码列表参见我的专栏文章。

下面是一个使用freopen函数的简单例子。这个例子中将标准输入流stdin重定向到一个名为foo.txt的文件,并从该文件中读取数据:

#includestdio.h

intmain(){

inta[5];

inti;

//将标准输入流stdin重定向到文件foo.txt

freopen("foo.txt","r",stdin);

//scanf函数从stdin标准输入流中接受输入

//stdin本来应该指向键盘

//但是被重定向到foo.txt

//所以scanf函数就从foo.txt中读取数据

for(i=0;i5;i++)

scanf("%d",a);

//输出读取到的数据

for(i=0;i5;i++){

printf("%d",a);

}

}

在这个例子中,我们首先使用freopen函数将stdin重定向到foo.txt文件。然后,我们使用scanf函数从重定向后的stdin读取数据,这些数据实际上来自foo.txt文件。最后,我们使用printf函数输出读取到的数据。

需要注意的是,如果foo.txt文件不存在或无法访问,freopen函数会失败,并返回NULL。在实际编程中,你应该检查freopen函数的返回值,以确保文件已成功打开。同时,你也应该检查scanf、printf等函数的返回值,以确保输入或输出操作已成功完成。

freopen函数的应用场景

(一)改变当前文件的打开模式

使用freopen可以将一个已经打开的文件,关闭后重新打开,并改成新的访问模式

#includestdio.h

intmain(){

FILE*file=fopen("foo.txt","r");

if(file==NULL){

perror("Errormsg:");

return1;

}

file=freopen("foo.txt","w",file);

if(file==NULL){

perror("Errormsg:");

return1;

}

fprintf(file,"输入数据到foo.txt文件。\n");

fclose(file);

return0;

}

(二)将标准流重定向到文件

可以将标准流stdin、stdout或stderr重定向到到文件。

#includestdio.h

intmain(){

freopen("foo.txt","w",stdout);

printf("这段话将输出到foo.txt文件。\n");

freopen("bar.txt","r",stdin);

intnum;

scanf("%d",num);

printf("%d\n",num);

//bar.txt中的数据输出到foo.txt中。

  fcloseall();

return0;

}

(三)替换文件指针指向的文件

将已存在的文件指针,通过freopen函数使其指向一个新的文件。

#includestdio.h

intmain(){

FILE*fp=fopen("file1.txt","r");

if(fp==NULL){

perror("Errormsg:");

return1;

}

//假设我们想要让fp指向另一个文件

fp=freopen("file2.txt","r",fp);

//现在fp指向file2.txt

charch;

while((ch=fgetc(fp))!=EOF){

putchar(ch);

}

fclose(fp);

return0;

}

(四)临时更改文件以进行错误日志记录

你可以使用freopen来临时改变标准错误流,以便将错误信息保存到日志文件中。

#includestdio.h

intmain(){

FILE*errFile=freopen("error.log","a",stderr);

fprintf(stderr,"错误信息保存到error.log文件中\n");

return0;

}

(五)在读取文件后追加内容

你可以使用freopen在读取文件内容后,不关闭文件,而是直接以追加模式重新打开文件,以便添加新内容。

#includestdio.h

intmain(){

FILE*file=fopen("data.txt","r");

//假设我们读取了文件内容...

//现在我们想要在文件末尾追加内容

file=freopen("data.txt","a",file);

fprintf(file,"新数据追加到data.txt尾部。\n");

fclose(file);

return0;

}

被重定向的标准流如何重新恢复?

标准流被重定向到文件,虽然很方便,特别是将错误信息保存到日志文件,或者从文件中输入内容到另一个文件。但是有一个问题,就是如何将标准流恢复到本来的功能,比如如何让stdin恢复到指向键盘,stdout和stderr恢复到显示器屏幕。

我们现在介绍下在windows系统如何恢复标准流。需要用到这几个函数:_fileno()、_dup()和_dup2()函数。

函数原型:

int_fileno(FILE*fp);

int_dup(intfd);

int_dup2(intfd1,intfd2);

1)_fileno函数用来获取标准流的文件说明符然后传给_dup或_dup2函数,用法:

intfd_stdout=_fileno(stdout);

2)_dup函数中的参数就是用_fileno函数获取的某个标准流的文件说明符,可以为这个标准流创建一个副本,这样后期需要将标准流恢复到原本功能时,就可以用创建的副本来恢复。

intbak=_dup(fd_stdout);

stdin的文件说明符是0,stdout的文件说明符就是1,stderr的文件说明符就是2。

)_dup2函数的作用就是用fd1所对应的标准流强制覆盖fd2所指向的文件对象。其用法类似于freopen函数。但是freopen函数无法恢复被重定向的标准流,但是_dup2函数可以。例如:

_dup2(bak,_fileno(stdout));

就可以用之前_dup函数保存的stdout副本强制覆盖被重定向的stdout,然后就恢复了stdout,重新指向了屏幕。

下面是完整的例子:

#includestdio.h

#includeio.h

#includestdlib.h

intmain(){

//获取标准输出流stdout的“文件说明符”

intfd=_fileno(stdout);

//保存stdout的副本

intbak=_dup(fd);

FILE*fp;

fp=fopen("foo.txt","w+");

fprintf(fp,"我会在foo.txt里。\n");

fflush(fp);

//stdout被重定向到foo.txt

fp=freopen("foo.txt","a+",stdout);

printf("\n我本应输出到屏幕结果却输出到foo.txt里。\n");

fflush(stdout);

//强制恢复stdout原本指向

_dup2(bak,_fileno(stdout));

printf("我又被输出到屏幕。\n");

fcloseall();

}

程序运行后效果如下图:

freopen_s函数

和之前介绍的fopen_s函数类似,所有安全版本函数都将原本函数的返回值作为输出参数,而将返回值用来作为当函数执行失败时返回错误信息。

输出参数和输入参数的概念,如果不了解,可以查看我真的专栏文章:《c语言函数不简单,参数也分输入输出?值和指针的传递只是表面!》。

函数原型如下:

errno_tfreopen_s(

FILE**pfp,

constchar*fileName,

constchar*mode,

FILE*redirectStream

);

参数解释如下:

pfp

FILE指针的指针,用来存储函数返回时指向重新打开的文件流对象的指针。

fileName

要重新打开的文件的名称。

mode

重新打开时的访问模式。

oldStream

将被重新指向filename。

返回值

如果成功,则为零;否则为错误代码。如果发生错误,则文关闭原始文件,新文件不被打开,stream的值是NULL。错误代码的详细信息可以用perror函数输出。

freopen_s函数通常用于将stdin、stdout和stderr重定向到另一个文件。

例子如下:

#includestdio.h

#includeerrno.h

intmain(){

FILE*fp;

errno_terr;

err=fopen_s(fp,“foo.txt”,”w+”);

if(err!=0){

perror(“errmsg:”);

return-1;

}

err=freopen_s(fp,”foo.txt”,”r+”,fp);

if(err!=0){

perror(“errmsg:”);

return-1;

}

//...

fclose(fp);

}

这是一个非常简单的例子,先用fopen_s函数打开foo.txt,然后再次重新打开foo.txt,目的就是为了切换文件访问模式,两个函数用的安全版本。因为使用起来比较简单,就不举太多例子浪费篇幅了。

总结

本章最重要的就是freopen函数的应用场景要熟练使用,同时也适用于freopen_s函数(是相同的)。同时,如何恢复标准流的重定向也是非常重要的,很多C程序员都不知道如何实现。本文介绍的是windows系统的方法,linux系统也是类似,都需要通过底层的函数,比如unistd.h头文件(linux)的dup函数来保存文件说明符,然后强制恢复。目前我写的基本都是面向初级C程序员的内容,后期在中级文章里会以为linux环境的操作为主。

段誉,年月26日,写于合肥。

分享 转发
TOP
发新话题 回复该主题