C文件操作
文件函数说明
此为基础篇文件操作,涉及基础使用函数:
fopen、fclose、fgetc、fgets、getc、fputcs、getchar、putchar、puts、putc、fgetc、ungetc等函数说明。
相关函数说明:
fopen
函数说明: fopen
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fopen
* 函数入口:
* pathname:打开文件的绝对路径
* mode:文件打开模式
* 函数出口:
* FILE:返回该文件的指针(句柄),错误打开返回NULL
* 函数说明:
* 按照指定打开方式打开指定路径的文件
* ***************************/
FILE *fopen(const char *pathname, const char *mode);
以下内容区分大小写!!!
以下内容区分大小写!!!
以下内容区分大小写!!!
参数 | 说明 |
---|---|
r | 只读打开方式,文件必须存在 |
r+ | 读写打开方式,文件必须存在,当文件被写入时会在当前流指针处进行写入,如果当前指针流处有字符将会被覆盖。 |
rb | 只读打开二进制文本,该文件必须存在 |
rb+ | 读写打开二进制文本,该文件必须存在 |
当文件被写入时会在当前流指针处进行写入,如果当前指针流处有字符将会被覆盖。 | |
w | 以新建只写方式打开,文件已存在内容将会被清空 |
w+ | 因新建读写方式打开,文件已存在将会被清空 |
wb | 以新建写二进制文件方式打开,文件已存在将会被清空 |
wb+ | 以新建读写二进制文件方式打开,文件已存在将会被清空 |
a | 以追加或新建文件方式打开,文件只允许在文件末尾追加 |
a+ | 以可读、追加或新建文件方式打开,文件只允许在文件末尾追加 |
ab | 以追加或新建二进制文件方式打开,件只允许在文件末尾追加 |
ab+ | 以可读,追加或新建二进制文件方式打开,件只允许在文件末尾追加 |
fclose
函数说明: fclose
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fclose
* 函数入口:
* stream 文件流
*
* 函数出口:
* 如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
* 函数说明:
* 关闭流 stream。刷新所有的缓冲区。
* ***************************/
int fclose(FILE *stream);
fgetc
函数说明: fgetc
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fgetc
* 函数入口:
* stream:文件流
* 函数出口:该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发
* 读错误,则返回 EOF。
* 函数说明: 向指定流中读出一个字符
* ***************************/
int fgetc(FILE *stream);
fgets
函数说明: fgets
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fgets
* 函数入口:
* s:存储读出字符串大小的缓冲区
* size:读出大小
* stream:文件流
* 函数出口:
* 如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
* 如果发生错误,返回一个空指针。
* 函数说明: 向指定流中读出一个字串放入到变量s中
* ***************************/
char *fgets(char *s, int size, FILE *stream);
getc
函数说明: getc
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:getc
* 函数入口:
stream:文件流
* 函数出口:该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
* 函数说明:从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符递增移动。
* 默认只会执行n-1个字符,默认一个字符会在末尾加上\0
* ***************************/
int getc(FILE *stream);
ungetc
函数说明: ungetc
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:ungetc
* 函数入口:c:这是要被推入的字符。该字符以其对应的 int 值进行传递。
* stream:这是指向 FILE 对象的指针,该 FILE 对象标识了输入流。
* 函数出口:如果成功,则返回被推入的字符,否则返回 EOF,且流 stream 保持不变。
* 函数说明:把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符。
* ***************************/
int ungetc(int c, FILE *stream);
fputc
函数说明: fputc
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fputc
* 函数入口:c:这是要被写入的字符。该字符以其对应的 int 值进行传递。
* stream: 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符的流。
* 函数出口:如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
* 函数说明:把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
* ***************************/
int fputc(int c, FILE *stream);
fputs
函数说明: fputs
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:fputs
* 函数入口:str:这是一个数组,包含了要写入的以空字符终止的字符序列。
* stream:这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。
* 函数出口:该函数返回一个非负值,如果发生错误则返回 EOF。
* 函数说明:把字符串写入到指定的流 stream 中,但不包括空字符。
* ***************************/
int fputs(const char *s, FILE *stream);
fputs
函数说明: fputs
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:putc
* 函数入口:c:这是要被写入的字符。该字符以其对应的 int 值进行传递。
* stream:这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符的流。
* 函数出口:该函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
* 函数说明:把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。不会自动在文件末尾添加\0
* ***************************/
int putc(int c, FILE *stream);
fputs
函数说明: fputs
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:ungetc
* 函数入口:c:这是要被写入的字符。该字符以其对应的 int 值进行传递。
* 函数出口:该函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
* 函数说明:把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。
* ***************************/
int putchar(int c);
puts
函数说明: puts
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:puts
* 函数入口:str:这是要被写入的 C 字符串。
* 函数出口:如果成功,该函数返回一个非负值为字符串长度(包括末尾的 \0),如果发生错误则返回 EOF。
* 函数说明:把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
* ***************************/
int puts(const char *s);
feof
函数说明: feof
。
/*************头文件*****************/
#include <stdio.h>
/*****************************
* 函数名称:feof
* 函数入口:stream: 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
* 函数出口:当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
* 函数说明:测试给定流 stream 的文件结束标识符。
* ***************************/
int feof(FILE *stream);
示例代码:
说明: 示例中文件名称为test.txt,内部操作数据为以下内容
G P G G A , 092204.999 , 4250.5589 , S , 14718.5084 , E , 2 , 04 , 24.4 , 19.7 , M , , , , 0000 ∗ 1 F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000∗1FGPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
示例1:使用fgetc读到文件结束并逐个使用fputc打印
#include "stdio.h"
void main(void)
{
FILE *fp=fopen("./test.txt","r");
int c=0;
while(!feof(fp))
{
c=fgetc(fp);
fputc(c,stdout);
}
fclose(fp);
}
运行结果:
G P G G A , 092204.999 , 4250.5589 , S , 14718.5084 , E , 2 , 04 , 24.4 , 19.7 , M , , , , 0000 ∗ 1 F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000∗1FGPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
�
运行结果分析:
文件最后会多读出一次文件结束符
由于feof不会偏移指针,而且当使用feof进行判断文件结束时,是从文件流的0位置开始而不是文件中第一个字符的位置,当先使用feof判断文件结束符 在使用fgetc或者其他读函数(可向下偏移指针流),函数会多进行一次读取,而该次读取将会超过文件。从而读出未知符号。文件结束符不会出现在文件中,而是出现在文件流中。以下改进多读一次代码:
#include "stdio.h"
#define LOG(msg,...) printf("[%s][%d]:"msg"",__FUNCTION__,__LINE__,##__VA_ARGS__)
void main(void)
{
FILE *fp=fopen("./test.txt","r");//假设文件中写入strlen(123456)+1个字符即7个6+"\0"
int c=0;
LOG("%ld\r\n",ftell(fp));//起始位置验证指向流的0位置
while(1)
{
c=fgetc(fp);//每次指向流的位置加一
if(feof(fp))
{
break;
}
//fputc(c,stdout);//完美只打印123456
LOG("%c--%ld\r\n",c,ftell(fp));//将会打印7次(获取但获取当前指针流位置函数)
}
fclose(fp);
}
假设文件中有123456\0EOF
运行结果:
使用fputc打印:123456
使用LOG打印:
[main][19]:0
[main][30]:1–1
[main][30]:2–2
[main][30]:3–3
[main][30]:4–4
[main][30]:5–5
[main][30]:6–6
[main][30]:–7
运行结果分析:
可以看见LOG打印了7次而fputc值打印了6次,结果分析fputc
使用fgetc先指向文件流中1位置打印1,再指向文件流中的2位置打印2,依次向下直到遇见文件中\0该内容无法显示fputc展现因此默认为输出为空。
LOG分析,当fgetc指向文件流中1位置打印1此时使用ftell获取的文件流位置为1,依次类推,当文件流位置指向\0时文件流为7,而此时%c无法打印\0,此时再使用fgetc将会指向文件外内容,从而文件流中生成EOF,造成退出。
同理分析以上内容其实都是打印了7次,只是fputc没有凸显\0那次打印。
将LOG修改为此语句printf(“%c”,c);结果同fputc
以上方法完美修复了案例一种使用feof多读出一次的情况,该案例原始就是写入例如strlen(123456)+1(使用strlen+1将会将字符串结束符写入文件),因此读出了7次,想要修复该BUG只需要,使用先偏移指针,再使用feof。
示例2:fgets读取文件字符串
#include "stdio.h"
void main(void)
{
FILE *fp=fopen("./test.txt","r");
char stdbuf[6]={0};
fgets(stdbuf,5,fp);//读取到n-1、末尾或者错误会停止(只会读出5-1个字符)
fputs(stdbuf,stdout);
fputc('\n',stdout);
fclose(fp);
}
运行结果:
$GPG
(换行符\n)
示例3:getc位置递增演示
#include "stdio.h"
void main(void)
{
char stdbuf[6]={0};
FILE *fp=fopen("./test.txt","r");
int ch=getc(fp);
fputc(ch,stdout);
fputc('\n',stdout);// 第一次读出
ch=getc(fp);
fputc(ch,stdout);
fputc('\n',stdout);//第二次读出
ch=getc(fp);
fputc(ch,stdout);
fputc('\n',stdout);//第三次读出
fclose(fp);
}
运行结果:
$
G
P
(换行符\n)
示例4:fputs测试
#include "stdio.h"
void main(void)
{
char buf[1024]={0};
FILE *fp=fopen("./test.txt","r");
fgets(buf,1024,fp);//虽然读到的是1024个字节但是当遇到文件结束符将会停止
fputs(buf,stdout);
fclose(fp);
}
运行结果:
G P G G A , 092204.999 , 4250.5589 , S , 14718.5084 , E , 2 , 04 , 24.4 , 19.7 , M , , , , 0000 ∗ 1 F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000∗1FGPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
示例5:getchar与putchar
#include "stdio.h"
void main(void)
{
char buf[1024]={0};
int ch=getchar();
ch=putchar(ch);
putchar('\n');
printf("%c\r\n",ch);
}
运行结果:(以在终端输入 5为示例)
5
5
5
示例6:puts与putc
#include "stdio.h"
void main(void)
{
int a='a';
putc(a,stdout);//默认以int强转字符输出
putc('\n',stdout);//打印\n
puts("123456");//默认输出到标准输出流
}
运行结果:
a
123456
示例7:fgetc
#include "stdio.h"
void main(void)
{
int ch=getc(stdin);
printf("%c\r\n",ch);
}
运行结果:(以终端输入5位例)
5
5
示例8:ungetc
#include "stdio.h"
int main ()
{
FILE *fp;
int c;
char buffer [256];
fp = fopen("test.txt", "r");
if( fp == NULL )
{
perror("打开文件时发生错误");
return(-1);
}
while(1)
{
c = getc (fp);
if(feof(fp))
{
break;
}
if( c == '$' )
{
ungetc ('+', fp);
}
else
{
ngetc(c, fp);
}
fgets(buffer, 255, fp);
fputs(buffer, stdout);
}
return(0);
}
运行结果:
+GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,0000*1F$GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
结果分析:
只会将流中的字符替换,不会修改文件中的字符。
示例9:r+是追加还是新建?
#include "stdio.h"
int main ()
{
FILE *fp;
int c;
char buffer [256];
fp = fopen("test.txt", "r+");
fputs("123456",fp);
fclose(fp);//将流指向的位置偏移回来
fp = fopen("test.txt", "r");
fgets(buffer,255,fp);
fputs(buffer,stdout);
fclose(fp);
return(0);
}
运行结果:
123456,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,0000*1F$GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
运行结果分析:
新写入的会进行覆盖式写入 如果不覆盖需要遍历到文件尾的位置进行写入。
示例10:fgets遇\0结束?
#include "stdio.h"
int main ()
{
FILE *fp;
int a=0;
int c=0;
char buf[]="$GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F$GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,, NULL";
char buffer [1024];
fp = fopen("test.txt", "w+");//新建文件
fwrite(buf,strlen(buf)+1,1,fp);//注意使用strlen+1将会在文件尾默认写入\0
fclose(fp);//关闭将流的指针复位
fp = fopen("test.txt", "a+");//在文件尾追加如123456
fputs("123456",fp);
fclose(fp);//复位流的位置
fp = fopen("test.txt", "r");
while(1)//使用fgetc读
{
c=fgetc(fp);
if(feof(fp))
{
break;
}
a++;
fputc(c,stdout);
}
printf("\r\n");
printf("%d\r\n",a);//验证读大字节大小
fclose(fp);//复位流指针
fp = fopen("test.txt", "r");//使用fgets读
fgets(buffer,1024,fp);
fputs(buffer,stdout);
fclose(fp);
return(0);
}
运行结果:
G P G G A , 092204.999 , 4250.5589 , S , 14718.5084 , E , 2 , 04 , 24.4 , 19.7 , M , , , , 0000 ∗ 1 F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000∗1FGPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL123456
145
G P G G A , 092204.999 , 4250.5589 , S , 14718.5084 , E , 2 , 04 , 24.4 , 19.7 , M , , , , 0000 ∗ 1 F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000*1F GPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M,,,,0000∗1FGPGGA,092204.999,4250.5589,S,14718.5084,E,2,04,24.4,19.7,M, NULL
运行结果分析:
可以看到使用fgetc打印了出了123456,而使用fgets没有打印出123456,原因就是fgets遇到\0将不在打印。
示例11:fgets从标准输入流写入时不足设置长度将会读入回车
#include "stdio.h"
int main ()
{
char a[10];
for (int i = 0; i < 10; ++i)
a[i] = '1';
fgets(a, 5, stdin);
for (int i = 0; i < 10; ++i)
printf(":[%d]%c:\n", i,a[i]);
return(0);
}
运行结果:
当从终端输入55555时效果
:[0]5:
:[1]5:
:[2]5:
:[3]5:
:[4]:
:[5]1:
:[6]1:
:[7]1:
:[8]1:
:[9]1:
当从终端输入5时效果:
5
:[0]5:
:[1]
:
:[2]:
:[3]1:
:[4]1:
:[5]1:
:[6]1:
:[7]1:
:[8]1:
:[9]1:
运行结果分析:
此例中fgets中要求获取5个字符大小,到时当输入5个字符时只会获取4个另一会添加一个\0.
当输入字符数小于n-1时将会将输入时的回车符读入。