目录
该节主要讲述使用c语言如何实现文件的创建、读取、修改、写入文本文件以及二进制文件的。
一、打开文件
-
fopen()
函数:
函数定义
函数定义:FILE *fopen(const char *pathname, const char *mode);
可以使用 fopen()
函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。其中pathname
是一个字符串,是要打开的文件的文件名,mode
是访问模式,具体如下:
Mode | Description |
---|---|
r | 读取文件。 |
r+ | 打开文件实现读取与写入。 |
w | 如果文件不存在,则创建文本文件并写入,程序会从文件的开头写入。如果文件存在,将文件截断为0长度(清空文件),再重新写入。 |
w+ | 打开文件实现读取和写入。如果文件不存在就创建,如果存在就截断(清空)重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
注意:++
mode
:The argument mode points to a string beginning with one of the above sequences (possibly followed by additional characters)++
也就是说:r --> rwere···
只会识别符合上述字符串的前几位,后面的不会在意,并且不会报错。
如果处理的是二进制文件,在 Linux 环境下不需要使用,而在移植到 Windows 环境下时,则需要加b
,使用下面的命令:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
-
errno
如果函数调用成功的话,就会返回一个 FILE
指针,否则失败的话,会返回一个NULL
(空指针),并且 errno
会被设置以表示出现的错误。errno
是一个全局变量。
查看定义:
vim /usr/include/asm-generic/errno-base.h
vim /usr/include/asm-generic/errno.h
errno
以前是整型变量,经后来多次重构之后变成了宏。
从上面的宏定义可以看出,使用errno
的时候要包含头文件errno.h
即可。该头文件也包含了errno-base.h
。
示例:验证 errno
不是一个整型变量
#include <errno.h>
errno; //程序时运行不起来的,这里只看预处理后的情况
实例演示:errno
输出
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4
5 int main(int argc, char *argv[])
6 {
7 FILE *fp;
8 fp = fopen("tmp", "r"); //"tmp" isn't exist in there
9
10 if(fp == NULL)
11 {
12 fprintf(stderr, "fopen is wrong! errno = %d\n", errno);
13 exit(1);
14 }
15
16 puts("OK!");
17
18
19 exit(0);
20 }
输出结果:
查看errno.h
,等于2时表示++要读取的文件不存在++:
那么问题又来了,errno
那么多值,怎么能记得住,要是可以直接打印输出“error message”错误信息就更好了。使用perror
和strerror
即可实现输出错误信息。
perror
先看定义:
SYNOPSIS
#include <stdio.h> //包含的头文件
void perror(const char *s); //语法格式形参为字符指针
#include <errno.h>
const char * const sys_errlist[];
int sys_nerr;
int errno; /* Not really declared this way; see errno(3) */
示例代码:
//将上面的第12行代码改为:
perror("fopen");
输出结果:
strerror
定义:
SYNOPSIS
#include <string.h>
char *strerror(int errnum); //形参是 errno
const char *strerrorname_np(int errnum);
const char *strerrordesc_np(int errnum);
int strerror_r(int errnum, char *buf, size_t buflen);
/* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
/* GNU-specific */
char *strerror_l(int errnum, locale_t locale);
示例代码:
//包含头文件
#include <string.h>
fprintf(stderr, "fopen(): %s !\n", strerror(errno));
输出结果:
-
判断
fopen
一次可以最多打开多少文件
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4
5 int main()
6 {
7 FILE *fp = NULL;
8 int count = 0;
9
10 while(1)
11 {
12 fp = fopen("tmp", "r");
13
14 if(fp == NULL)
15 {
16 perror("fopen()");
17 break;
18 }
19
20 count++;
21 }
22
23 printf("count = %d \n", count);
24
25 exit(0);
26 }
这里发现最多可打开 1021 次,对吗?不对的!但是达到打开上限是真的。至于这个上限是多少次,分析如下:
在不更改当前默认环境的情况下,一个进程默认打开了3个流(stream
)-->stdin stdout stder
,而文件打开也属于是流操作,因此上限一共是:1021 + 3 = 1024。
可以使用命令ulimit -a
查看:
jxs@jxs-ubuntu:~/Desktop/unix_c$ ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15283
max locked memory (kbytes, -l) 497896
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 15283
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
//更改的话,类似使用:
ulimit -n 2048
-
关于使用
fopen
创建的文件的权限分配
查看权限:
6 6 4,为什么呢?
原因:权限分配 --> 0666 & ~umask
(0666)8 --> (110 110 110)2
,~umask --> ~(000 000 010)2 = (111 111 101)2
;
(110 110 110)2 & (111 111 101)2 = (110 110 100)2
=++(6 6 4)++
可见umask
值越大,创建的文件的权限越低。
二、关闭文件
fclose()
函数:
函数定义:int fclose(FILE *stream);
如果流参数是非法指针,或者是已传递给先前调用的描述符,那么fclose()
的行为就是未定义的。如果成功关闭文件,fclose( )
函数返回零,如果关闭文件时发生错误,函数返回 EOF
。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF
是一个定义在头文件 stdio.h
中的常量。
由于
fopen
返回的指针是存储在堆上的,属于动态分配的内存,因此要使用fclose
函数对内存的释放,free
关键词就是定义在该函数中的。
示例代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
5
6 int main(int argc, char *argv[])
7 {
8 FILE *fp;
9 fp = fopen("tmp", "r"); //"tmp" isn't exist in there
10
11 if(fp == NULL)
12 {
13 // fprintf(stderr, "fopen is wrong! errno = %d\n", errno);
14 // perror("fopen");
15 fprintf(stderr, "fopen(): %s !\n", strerror(errno));
16
17
18 exit(1);
19 }
20
21 puts("OK!");
22 fclose(fp); //谁打开谁关闭
23
24 exit(0);
25 }
输出结果:
三、写入文件
- 使用
fputc()
函数时:
函数定义:int fputc( int c, FILE *fp );
该函数将字符c写入到fp所指向的输出流中。如果成功写入会返回写入的字符,否则会返回
EOF
可以使用下面的函数将以NULL
结尾的字符串写入到流中。
- 使用
fputs()
函数时:
函数定义:int fputs( const char *s, FILE *fp );
该函数可以将字符串
s
写入到fp
执行的输出流中。如果写入成功则函数返回非负值,否则返回EOF
,同时可以使用函数int fprintf(FILE *fp, const char* format, ···)
把一个字符串写入到文件中。
EOF
是宏常量,int
类型的负值整数常量表达式。
该值通过fgets
以及相似的函数返回值来表示文件的末尾,EOF
默认值为-1
.
示例程序:
#include <stdio.h>
int main()
{
FILE *fp = NULL;
//定义对文件的操作权限
fp = fopen("/tmp/test.txt", "w+"); //打开/tmp/test.txt文件实现读取和写入
fprintf(fp, "This is a test for fprintf···\n");
fputs("This is a test for fputs···", fp);
fcose(fp);
exit(0);
}
注意:请确保您有可用的 tmp
目录,如果不存在该目录,则需要在您的计算机上先创建该目录。/tmp
一般是 Linux 系统上的临时目录,如果你在 Windows系统上运行,则需要修改为本地环境中已存在的目录,例如: C:\tmp、D:\tmp
等。
实例1:实现文件的复制
//实现的目标:./mycp src dest
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// 这里main 函数要传参:src dest
int main(int argc, char *argv[])
{
//定义两个文件流
FILE *fps; //源文件
FILE *fpd; //目标文件
int ret;
//如果传入的参数较少,即 < 3 个,就给出提示,并退出程序
if(argc < 3)
{
printf("Error Usage --> %s <source file> <destination file>");
//break; //指跳出循环,并不会退出程序
exit(1); //由于输入参数不够因此需要退出程序
}
//打开两个文件并返回指针类型,并检验是否打开成功
fps = fopen(argv[1], "r"); //argv[1] --> main 函数中的第一个参数且为字符串
if(fps == NULL)
{
perror("fopen");
exit(1); //程序结束
}
fpd = fopen(argv[2], "w");
if(fpd == NULL)
{
perror("fopen");
exit(1); //程序结束
}
//打开成功后:src --> dest 实现数据传输
while(1)
{
//使用fgetc实现字符获取
ret = fgetc(fps);
//直到end of file(文件末尾),退出循环
if(ret == EOF)
break;
//使用fputc实现字符写入,但是fputc(int c, FILE *stream),因此fgetc需要接收一个返回值传入该函数中。
fputc(ret, fpd);
};
//谁打开谁关闭
fclose(fpd); //优先关闭被写入的
fclose(fps);
exit(0);
}
实例3:计算文件大小(size)
//实现的原理:使用 fgetc() 读取文件所包含的字符数,1个字符 --> 1 byte
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
//命令实现:./get_size file ,因此需要 main 函数进行传参
int main(int argc, char *argv[])
{
FILE *fp;
int count; //计数计算文件所含的字符数
fp = fopen(argv[1], "r"); //指针接收 fopen 的打开情况
//判断是否打开成功
if(fp == NULL)
{
perror("fopen");
exit(1);
}
//读取文件所含有的字符数
while(fgetc(fp) != EOF)
{
count++;
}
printf("The size of this file is %d Bytes \n", count);
fclose(fp);
exit(0);
}
四、读取文件
上面的程序会分别通过fprintf
和fputs
函数将相应的文本写入到test.txt
文件中。那么如何读取文件中的内容呢?
- 从文件中读取单个字符的函数:
int fgetc(FILE *stream);
fgetc
函数将从fp指向的输入文件中读取一个字符,返回值是读取的字符,如果发生错误返回EOF
。
- 从文件中读取一个字符串的函数:
char fgets(char *s, int n, FILE *stream);
函数将从
fp
指向的文件中读取n - 1
个字符,会将读取的字符串存储到缓冲区s
中,并在最后追加一个NULL
-->'\0'
字符来终止字符串。如果这个函数在读取到最后一个字符串之前就遇到\n
或者文件的末尾EOF的话,只会返回所读取到的字节,也包括换行符。也可以使用int fscanf(FILE *fp, const char *format, ···)
函数读取文件中的字符串,但是遇到空格或者换行符时会停止读取。
比如:文件中有abcd'\n'
使用fgets
函数读取时,假设n = 5,那么完全读取该文件++需要读两次++
- 1 -->
a b c d '\0'
- 2 -->
'\n' '\0'
示例函数:
//关于返回值:fgets() 成功的话返回字符串s, 错误或者读到文件尾已经没有字符可以读取了返回 NULL
while(fgets(buff, SIZE, fps) != NULL)
{
fputs(buff, fpd);
};
五、fread
&fwrite
1.1. 定义
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);
fread()
与fwrite
从流中读取数据的nmemb
项,每一项的数据大小为size bytes
,将读取到的数据存储至ptr
中,读入的大小为(size * nmemb) bytes
.- 返回值为读取的项数,当
size = 1
时,等同于传输的字节数,如果读取失败或者读到文件尾,返回值是short item count (or zero)
1.2. 实例
//实现的目标是:使用 fread 与 fwrite 实现文件复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int main(int argc, char **argv)
{
FILE *fps;
FILE *fpd;
char buff[SIZE];
int n;
if(argc < 3)
{
printf("Usage Error!\n");
exit(1);
}
fps = fopen(argv[1], "r");
if(fps == NULL)
{
perror("fopen");
exit(1);
}
fpd = fopen(argv[2], "w");
if(fps == NULL)
{
perror("fopen");
exit(1);
}
while((n = fread(buff, 1, SIZE, fps)) > 0)
{
fwrite(buff, 1, n, fpd); //使用n的原因是,在size = 1时,函数fread()返回值就是实际读取的项数,如果不是n的话,是SIZE的话,如果fps对应的文件字符数小于SIZE的话,最后fpd对应的文件就会有多余的字符出现
};
fclose(fps);
fclose(fpd);
exit(0);
}
标签:11,文件,include,读取,int,写入,fp,fopen
From: https://www.cnblogs.com/jxsme/p/17514695.html