-
返回结果
- 若执行成功:返回FILE 指针,一个非负整数。
- 若执行失败:返回NULL,并设置errno为对应的非负整数。
- errno为全局变量,所以想获得正确的出错原因,得立马打印,否则可能会被其他进程修改。
- errno在现在的系统中,很多时候是定义为一个宏。
-
参数解析
- path:文件所在路径。
- mode:一个字符串用于表达该文件的打开方式。
- r:在文件起始位置打开文件,只读。若文件不存在,则执行失败。
- r+:在文件起始位置打开文件,可读可写。若文件不存在,则执行失败。
- w:若文件存在,则清空,指针指向文件头;若不存在该文件,则创建该文件,指针指向文件头。只写。
- w+:同上,只是可读可写。
- a:若文件不存在,则创建文件。若存在,则指针指向文件末尾。只写。
- a+:若文件不存在,则创建文件。若存在,那最初读的时候,指针指向文件头。最初写的时候,指针指向文件尾。
- b:这使得标准I/O系统可以区分文本文件和二进制文件。而unix内核(包括linux)并不对这两种文件进行区分,所以在linux中,b这个选项加与不加并没有意义。但如果这个程序需要从linux移植到windows中去,那需要加上。
-
注意事项
- mode对应的字符串,系统会从前往后读,截取满足规定的头部,其余部分丢弃。如mode=“r+sffsfsff”,这不会报错,系统会认为mode=“r+”,其余部分丢弃。
- 使用fopen得到的FILE指针所指向的内存块,存放在堆中。
- fopen打开的流,记得要用fclose关闭。
- 一般有互逆操作的创建函数,它的指针指向的内存块都存放在堆中的。
- 当使用头文件中的函数时,man 该函数,该函数必须包含哪些头文件,也必须在调用它的函数中全部包含。否则会出错。test1.c的完整代码如下:
int main()
{
int *p = malloc(sizeof(int));
return 0;
}
编译运行时,出现如下错误:
按道理,malloc返回void * ,可以赋值给任何类型的指针,可这里还是报错。使用man 3 malloc,得到如下信息:
可知,malloc需要<stdlib.h>。我们在源代码中添加头文件,就会顺利编译通过。
-
简析:
- 如上图所示,perror需要包含头文件<stdio.h>,且使用到了全局变量,errno、sys_nerr、sys_errlist[]。
- perror函数的会在参数s字符串之后加上,与errno对应的具体出错原因。
-
strerror
例子如下:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int
main()
{
FILE * fp;
fp = fopen("tmp","r");//当前目录下没有tmp文件,则以r的方式打开,会失败。
if(fp==NULL)
{
fprintf(stderr,"fopen() failed !errno=%d\n ",errno);
perror("perror output:");
fprintf(stderr,"strerror output:%s\n",strerror(errno));
exit(1);
}
puts("OK!");
return 0;
}
输出结果如下:
通过如下程序不断的打开文件流,直到出错,并将已打开的流的数量输出。
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int
main()
{
int count = 0;
FILE * fp;
while(1)
{
fp = fopen("tmp","w");
if(fp==NULL)
{
// fprintf(stderr,"fopen() failed !errno=%d\n ",errno);
// perror("ffff");
fprintf(stderr,"fopen failed!%s\n",strerror(errno));
break;
}
count++;
}
printf("%d\n",count);
return 0;
}
一个进程默认还打开了stdin、stdout、stderr三个流,所以3+1021=1024个流。即一个进程最多只能打开1024个流。
通过ulimit -a,可以得知系统设置了进程所能打开流的上限。
open files 对应的选项为 -n,就可以通过ulimit -n 修改上限值。
-
fgetc、fputc
-
int fgetc(FILE * stream )
- 返回值:从指定的流中读取下一个字符,并把unsigned 的字符强制转换为int。若读取失败或者读到EOF,就返回EOF。
-
int fputc(int c, FILE *stream);
- 将int类型的c强制转换为unsigned char,输出到指定的流中。
- 返回值。输出失败,返回EOF。输出成功,则把c返回。
-
利用fgetc、fputc实现自己的cp程序如下:
-
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
int main(int argc,char * argv[])
{
if(argc!=3)
{
//命令格式不正确的话,给出提示信息。
fprintf(stderr,"Usage....%s <source file> <dest file>",argv[0]);
exit(1);
}
FILE * fps,*fpd;
int ch; //这里不能用char,因为要保存EOF,很多系统的实现都为-1.
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("source file open failed! ");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps); //fps已经打开,fpd没打开成功的话,这里必须关闭。
perror("des file open failed ! ");
exit(1);
}
while(1)
{
ch = fgetc(fps);
if(ch == EOF)
break;
fputc(ch,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
//实现统计一个文件的字符数
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char * argv[])
{
if(argc != 2)
{
fprintf(stderr,"Usage---%s <source file>",argv[0]);
exit(1);
}
int count = 0;
int ch;
FILE * fps;
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("source file open failed !");
exit(1);
}
ch = fgetc(fps);
while(ch !=EOF)
{
count++;
ch = fgetc(fps);
}
printf("count = %d \n",count);
exit(0);
}
-
gets
gets不安全,存在溢出的可能,不建议使用gets,建议使用fgets。
-
fgets、fputs
-
char * fgets(char *s, int size, FILE *stream);
- 功能介绍:从指定的流中读取不多于size-1个字节存放在s中。
- 返回值:成功返回s。失败或者这一行没有读取到有效字符,返回NULL。(EOF不属于有效字符)
- 读取终止条件: 读到’\n’ 或者 读取到size - 1个字符。
- 注意事项:
- 若size=5;要读取的字符为“abcdefjg”,则读取"abcd",再在s末尾添加一个’\0’。
- 若size=5;要读取的字符为"abc",则读取"abc\n",再在末尾加上’\0’。
- 若size =5;要读取的字符为,“abcd”,则读取“abcd‘’,再在末尾添加一个’\0’。还剩下一个\n没有读取,存储在缓冲区中,等待读取。
-
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#define BUFFSIZE 1024
int main(int argc,char * argv[])
{
if(argc!=3)
{
fprintf(stderr,"Usage....%s <source file> <dest file>",argv[0]);
exit(1);
}
char buf[BUFFSIZE];
FILE * fps,*fpd;
int ch;
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("source file open failed! ");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);
perror("des file open failed ! ");
exit(1);
}
while(fgets(buf,BUFFSIZE,fps) != NULL)
{
fputs(buf,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
-
fread、fwrite
-
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 功能介绍:fread从指定流中,读取nmemb个数据块,每个数据块大小为size,然后存放在ptr指向的内存中。
- 返回值:返回已经完整的成功的读取的数据块个数。如size=2,那读取了20Byte的数据,返回10;读取了19Byte的数据,返回9。
-
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
- 从ptr中读取nmemb个数据块,每个数据块size字节,然后输出到指定的流中。
- 返回值:成功完整输出的数据块个数。
-
注意事项:
- fread、fwrite容易出错。
- fread(buf,1,10,fp)-------A
fread(buf,10,1,fp)-------B
第一种情况:若数据量足够,则A、B都不出问题。
第二种情况:加入数据量只有五字节,则A读取后,返回5;B读取后返回0。由于读取0到9个字节都是返回0,所以此时不知道到底读了几个字节。 - 所以,使用尽可能使用size=1的fread函数,避免许多问题。
- 使用fread、fwrite实现cp的代码如下:
-
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#define BUFFSIZE 1024
int main(int argc,char * argv[])
{
if(argc!=3)
{
fprintf(stderr,"Usage....%s <source file> <dest file>",argv[0]);
exit(1);
}
char buf[BUFFSIZE];
FILE * fps,*fpd;
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("source file open failed! ");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);
perror("des file open failed ! ");
exit(1);
}
int n;
/*此处非常需要注意!
若使用如下方式进行复制,就会出问题。极有可能使得复制得到的文件比源文件多内容。
while(fread(buf,1,BUFFSIZE,fps))
{
fwrite(buf,1,BUFFSIZE,fpd);
}
分析:这个文件的大小如果刚好是BUFFERSIZE的整数倍,那不会出问题。假如不是(大概率不是),那在最后一次执行fread函数(也就是那次fread读取到文件尾)时,buf中必然没有BUFFSIZE个字符,但使用fwrite写入时,仍然会写入BUFFSIZE个字符,这就导致复制得到的文件,内容变多。
*/
while((n = fread(buf,1,BUFFSIZE,fps)) >0)
{
fwrite(buf,1,n,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}