文件操作一(非常重要)
- 一、为什么使用文件?
- 二、什么是文件?
- 三、文件名(简单理解)
- 四、二进制文件和文本文件(重要)
- 五、流的概念(非常重要)
- 六、文件的打开和关闭
- 七、文件的顺序读写函数
- 八、文件的随机读写函数
- 九、 文件读取结束的判定
- 十、 文件缓冲区(重要)
一、为什么使用文件?
1.这个好理解,因为写的程序操作的数据只能是在内存中存储,要程序不断运行,数据才不丢失,为了更好的保存数据,以文件的形式把数据存储在磁盘上,更好的保存数据!
二、什么是文件?
1.程序分为数据文件和程序文件。
2.程序文件:包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows环境后缀为.exe)
3.数据文件:存储数据的文件。
三、文件名(简单理解)
⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。
四、二进制文件和文本文件(重要)
1.二进制文件:把在内存中存的数据不加转换,直接存到磁盘上的文件。
2.把内存中的数据拿出来转换成ASCII码的形式存储在外存中的文件称为文本文件。
int main()
{//以二进制的形式把数据存入内存int a = 10000;FILE* pf = fopen("test.txt", "wb");//把数据以二进制的形式存入到文件if (pf == NULL){return 1;}fwrite(&a, sizeof(a), 1,pf);fclose(pf);pf = NULL;return 0;
}
把二进制文件以二进制的形式打开,直接可以看出01串,这个就是直接把内存中的数据放到外存上(磁盘)。
五、流的概念(非常重要)
1.流
流这个概念太重要了,在这儿,我弱化概念,直接说明流是什么?流就是连接内存与外设的通道,比如说,一个外设(文件),它与内存是怎么连接的,就是通过流,流的本质就是创建了结构体,用来存储外设的信息,来连接内存,在外设(文件操作中),在内存中开辟了一块空间(文件信息区)是一个结构体,用来存储了文件的信息,而取出这个结构体的地址(指针),就可以对文件操作了,在vs2013中,流(结构体)的·信息如下:
当打开一个文件时,fopen函数,会为打开的这个外设自动创建文件信息区,也就是创建了一个结构体,通过返回这个结构体的地址,用指针来接收,就可以操作指针来控制文件(外设)和内存进行相关操作。
2.标准流
在printf打印函数时,把内存的数据显示到终端上了,在scanf函数输入数据时,从键盘上读取数据,把数据放入内存中,在这两个函数对外设操作时,外设的信息存在哪儿?也就是说为什么没有打开流,那是因为C语⾔程序在启动的时候,默认打开了3个流,具体如下:
(1)stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
(2)stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。
(3)标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为⽂件指针。C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的.
六、文件的打开和关闭
1.fopen(重要)
2.fclose(重要)
Return Value
fclose returns 0 if the stream is successfully closed. _fcloseall returns the total number of streams closed. Both functions return EOF to indicate an error.
int main()
{//如果文件打开失败,会返回null//.表示当前路径 ..上一级路径//w是覆盖写FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen:");return 1;}fclose(pf);pf = NULL;return 0;
}
七、文件的顺序读写函数
(1)先看第一组函数fgetc和fputc函数。
int fgetc( FILE stream );
Read a character from a stream (fgetc, fgetwc) or stdin (_fgetchar, _fgetwchar).
此函数从外设得到一个字符,保存到内存中
Return Value
fgetc and _fgetchar return the character read as an int or return EOF to indicate an error or end of file. fgetwc and _fgetwchar return, as a wint_t, the wide character that corresponds to the character read or return WEOF to indicate an error or end of file. For all four functions, use feof or ferror to distinguish between an error and an end-of-file condition. For fgetc and fgetwc, if a read error occurs, the error indicator for the stream is set.
fgetc和_fgetchar以int型返回读取的字符,或者返回EOF表示错误或文件结束。fgetwc和_fgetwchar以wint_t型返回与读取字符对应的宽字符,或者返回WEOF表示错误或文件结束。对于这四个函数,可以使用feof或ferror来区分错误和文件结束的条件。对于fgetc和fgetwc,如果发生读取错误,流的错误指示器会被设置。
int fputc( int c, FILE stream );
Writes a character to a stream (fputc, fputwc) or to stdout (_fputchar, _fputwchar).
此函数是把内存中存的一个字符送到外设中
Return Value
Each of these functions returns the character written. For fputc and _fputchar, a return value of EOF indicates an error. For fputwc and _fputwchar, a return value of WEOF indicates an error.
返回值 每个函数都返回所写的字符。对于fputc和_fputchar,返回值EOF表示错误。对于fputwc和_fputwchar,返回值WEOF表示错误。
int main()
{//给文件写内容FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}fputc('a', pf); //给文件写字符afputc('b', pf); //...fputc('c', pf); //...fputc('d', pf); //...for (int i = 0; i < 26; i++){fputc('a'+i, pf); //对应的操作是外设fputc('\n', pf);}/*for (int i = 0; i < 26; i++){fputc('a' + i, stdout); //显示到终端fputc('\n', stdout);}*/fclose(pf);pf = NULL;return 0;
}
int main()
{//读取文件的内容,放到内存中保存FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}int ch = fgetc(pf); //fgetc对应的操作是内存printf("%c", ch);ch = fgetc(pf);printf("%c", ch);ch = fgetc(pf);printf("%c", ch);ch = fgetc(pf);printf("%c", ch);fclose(pf);pf = NULL;return 0;
}
(2)写代码,完成拷贝操作!
//写代码,完成拷贝操作
int main()
{FILE* pfread = fopen("data1.txt", "r");if (pfread == NULL){perror("fopen->data1:");return 1;}FILE* pfwrite = fopen("data2.txt", "w");if (pfwrite == NULL){fclose(pfread);pfread = NULL;perror("fopen->data2:");return 1;}//数据的读写char ch = 0;while (ch = fgetc(pfread) != EOF){fputc(ch, pfwrite);}fclose(pfread);fclose(pfwrite);return 0;
}
(3)fgets和fputs函数
Get a string from a stream.
char *fgets( char *string, int n, FILE *stream );
Return Value
Each of these functions returns string. NULL is returned to indicate an error or an end-of-file condition. Use feof or ferror to determine whether an error occurred.
返回值
这些函数中的每一个都返回字符串。返回NULL表示发生了错误或文件结束的条件。可以使用feof或ferror来确定是否发生了错误。
Write a string to a stream.
int fputs( const char *string, FILE *stream );
Return Value
Each of these functions returns a nonnegative value if it is successful. On an error, fputs returns EOF, and fputws returns WEOF.
返回值
如果成功,每个函数都返回一个非负值。如果出现错误,则fputs 返回 EOF,fputws 返回 WEOF。
int main()
{FILE* pf = fopen("data1.txt", "w");if (pf == NULL){return 1;}//给流写一个字符,保存到文件中fputs("abcdef", pf);fputs("abcdef", pf);fclose(pf);pf = NULL;return 0;
}
int main()
{char arr[20];FILE* pf = fopen("data1.txt", "r");if (pf == NULL){return 1;}//从流中得到一个字符,存储在内存中fgets(arr, 5, pf);fclose(pf);pf = NULL;return 0;
}
int main()
{//向栈区申请20个字节的空间char arr[20];//从键盘获得数据保存到arr中fgets(arr, 5, stdin);//把arr中的数据写到屏幕上fputs(arr, stdout);return 0;
}
(4)fscanf ,fprintf ,scanf,printf
Read formatted data from a stream.
从流中读取格式化数据int fscanf( FILE *stream, const char *format [, argument ]… );
Return ValueEach of these functions returns the number of fields successfully
converted and assigned; the return value does not include fields that
were read but not assigned. A return value of 0 indicates that no
fields were assigned. If an error occurs, or if the end of the file
stream is reached before the first conversion, the return value is EOF
for fscanf or WEOF for fwscanf.返回值 每个函数都返回成功转换和赋值的字段数;返回值不包括已读取但未赋值的字段。返回值为0表示未分配任何字段。如果发生错误,或者在第一个转换之前达到了文件流的文件尾,fscanf的返回值为EOF,fwscanf的返回值为WEOF。
【Read formatted data from the standard input stream.】
int scanf( const char *format [,argument]… );
Return Value
Both scanf and wscanf return the number of fields successfully
converted and assigned; the return value does not include fields that
were read but not assigned. A return value of 0 indicates that no
fields were assigned. The return value is EOF for an error or if the
end-of-file character or the end-of-string character is encountered in
the first attempt to read a character.
返回值 scanf和wscanf都返回成功转换和赋值的字段数;返回值不包括被读取但未被赋值的字段。返回值为0表示未赋值任何字段。如果在第一次尝试读取字符时遇到错误、文件结束字符或字符串结束字符,则返回值为EOF。
Print formatted data to a stream.
int fprintf( FILE *stream, const char *format [, argument ]…);
Return Value
fprintf returns the number of bytes written. fwprintf returns the
number of wide characters written. Each of these functions returns a
negative value instead when an output error occurs.返回值 fprintf返回写入的字节数。fwprintf返回写入的宽字符数。当发生输出错误时,这些函数都会返回负值。
Print formatted output to the standard output stream.
//打印有格式的数据输出到标准输出流 int printf( const char *format [, argument]… );Return Value
Each of these functions returns the number of characters printed, or a
negative value if an error occurs.返回值 这些函数中的每一个都返回打印的字符数,如果发生错误,则返回负值。
struct Stu
{char name[20];int age;float score;
};int main()
{struct Stu s = { "zhangsan",20,90.5f };FILE* pf = fopen("data1.txt", "w");if (pf == NULL){return 1;}//写文件//从结构体获取数据//打印有格式的数据到流中fprintf(pf, "%s %d %.1f", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
struct Stu
{char name[20];int age;float score;
};int main()
{struct Stu s = {0};FILE* pf = fopen("data1.txt", "r");if (pf == NULL){return 1;}//读文件//从流中读取格式化的数据放到结构体中fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));fprintf(stdout, "%s %d %.1f", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
(5)fread ,fwrite
Reads data from a stream. //从流中读取数据,放到内存中
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );Return Value
fread returns the number of full items actually read, which may be
less than count if an error occurs or if the end of the file is
encountered before reaching count. Use the feof or ferror function to
distinguish a read error from an end-of-file condition. If size or
count is 0, fread returns 0 and the buffer contents are unchanged.返回值
fread 返回实际读取的完整项数,如果发生错误或达到 count 之前遇到文件末尾,则可能小于 count。使用 feof 或
ferror 函数区分读取错误和文件末尾条件。如果 size 或 count 为 0,则 fread 返回 0,缓冲区内容不变。
**
Writes data to a stream.
//写数据到流中
size_t fwrite( const void *buffer,size_t size, size_t count, FILE *stream ); Return Value fwrite returns
the number of full items actually written, which may be less than
count if an error occurs. Also, if an error occurs, the file-position
indicator cannot be determined.
返回值
fwrite返回实际写入的完整项数,如果发生错误可能会少于count。此外,如果发生错误,无法确定文件位置指示器。
**
struct Stu
{char name[20];int age;float score;
};int main()
{struct Stu s = { "zhangsan",20,90.5f };FILE* pf = fopen("data1.txt", "wb");if (pf == NULL){return 1;}//写文件fwrite(&s, sizeof(s), 1, pf);fclose(pf);pf = NULL;return 0;
}int main()
{struct Stu s = {0};FILE* pf = fopen("data1.txt", "rb");if (pf == NULL){return 1;}//读文件fread(&s, sizeof(s), 1, pf);printf("%s %d %.1f\n", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
(6)sscanf,sprintf
Read formatted data from a string.
//从字符串中读取有格式的数据
int sscanf( constchar *buffer, const char *format [, argument ] … );
Return ValueEach of these functions returns the number of fields successfully
converted and assigned; the return value does not include fields that
were read but not assigned. A return value of 0 indicates that no
fields were assigned. The return value is EOF for an error or if the
end of the string is reached before the first conversion.返回值
每个函数都返回成功转换和分配的字段数量;返回值不包括已读取但未分配的字段。返回值为0表示没有字段被分配。如果发生错误或在第一个转换之前到达字符串末尾,则返回值为EOF。
Write formatted data to a string.
//有格式的数据写到字符串 int sprintf( char
*buffer, const char *format [, argument] … );
Return Valuesprintf returns the number of bytes stored in buffer, not counting the
terminating null character. swprintf returns the number of wide characters stored in buffer, not counting the terminating null wide
character.返回值
sprintf返回存储在缓冲区中的字节数,不包括终止的空字符。swprintf返回存储在缓冲区中的宽字符数,不包括终止的空宽字符。
(7)scanf/fscanf/sscanf
printf/fprintf/sprintf
scanf - 指针标准输入(键盘)的格式化输入函数
printf-针对标准输出(屏幕)的格式化输出函数
fscanf - 针对所有输入流的格式化输入函数
fprintf - 针对所有输出流的格式化输出函数
sscanf-从一个字符串中读取一个格式化的数据
sprintf-把一个格式化的数据转换成字符串
struct S
{char name[20];int age;float score;
};int main()
{struct S s = { "zhangsan",20,36.1f };struct S tmp;char str[100];sprintf(str, "%s %d %f", s.name, s.age, s.score);printf("%s\n", str);sscanf(str, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));printf("%s %d %f", tmp.name,tmp.age, tmp.score);return 0;
}
八、文件的随机读写函数
(1)fseek
Moves the file pointer to a specified location.
//移动光标到特定的位置
int fseek( FILE stream, long offset, int origin );
/******************************************************************************/
Return Value
If successful, fseek returns 0. Otherwise, it returns a nonzero value. On devices incapable of seeking, the return value is undefined.
Parameters
stream
Pointer to FILE structure
offset
Number of bytes from origin
origin
Initial position
Remarks
The fseek function moves the file pointer (if any) associated with stream to a new location that is offset bytes from origin. The next operation on the stream takes place at the new location. On a stream open for update, the next operation can be either a read or a write. The argument origin must be one of the following constants, defined in STDIO.H:
SEEK_CUR
Current position of file pointer
SEEK_END
End of file
SEEK_SET
Beginning of file
You can use fseek to reposition the pointer anywhere in a file. The pointer can also be positioned beyond the end of the file. fseek clears the end-of-file indicator and negates the effect of any prior ungetc calls against stream.
When a file is opened for appending data, the current file position is determined by the last I/O operation, not by where the next write would occur. If no I/O operation has yet occurred on a file opened for appending, the file position is the start of the file.
For streams opened in text mode, fseek has limited use, because carriage return–linefeed translations can cause fseek to produce unexpected results. The only fseek operations guaranteed to work on streams opened in text mode are:
Seeking with an offset of 0 relative to any of the origin values.
Seeking from the beginning of the file with an offset value returned from a call to ftell.
Also in text mode, CTRL+Z is interpreted as an end-of-file character on input. In files opened for reading/writing, fopen and all related routines check for a CTRL+Z at the end of the file and remove it if possible. This is done because using fseek and ftell to move within a file that ends with a CTRL+Z may cause fseek to behave improperly near the end of the file.
返回值
如果成功,fseek返回0。否则,它返回一个非零值。在无法寻找的设备上,返回值是未定义的。
参数
stream
指向FILE结构的指针
offset
距离起点的字节数
origin
初始位置
备注
fseek函数将与stream相关联的文件指针(如果有)移动到距离起点offset字节的新位置。流上的下一个操作将在新位置进行。对于打开以进行更新的流,下一个操作可以是读取或写入。参数origin必须是以下常量之一,定义在STDIO.H中:
SEEK_CUR
文件指针的当前位置
SEEK_END
文件末尾
SEEK_SET
文件开头
您可以使用fseek将指针重新定位到文件中的任何位置。指针也可以定位到文件末尾之外。fseek清除文件结束指示符,并取消对流进行的任何先前ungetc调用的影响。
当打开文件以追加数据时,当前文件位置由最后一个I/O操作确定,而不是下一个写入的位置。如果在打开用于追加的文件上尚未进行任何I/O操作,则文件位置是文件的开头。
对于以文本模式打开的流,fseek的用途有限,因为回车换行转换可能导致fseek产生意外的结果。在以文本模式打开的流上,唯一保证可用的fseek操作是:
相对于任何起点值的偏移量为0的寻找。
使用从调用ftell返回的偏移值从文件开头开始寻找。
此外,在文本模式下,CTRL+Z被解释为输入的文件结束字符。在以读取/写入方式打开的文件中,fopen和所有相关例程都会检查文件末尾的CTRL+Z并尽可能地删除它。 这样做是因为使用fseek和ftell在以CTRL+Z结尾的文件中进行跳转可能会导致fseek在文件末尾附近行为不当。
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){return 1;}//fseek(pf, 0, SEEK_CUR);//char ch = fgetc(pf);//printf("%c\n", ch);//fseek(pf, 1, SEEK_CUR);//ch = fgetc(pf);//printf("%c\n", ch);//fseek(pf, -1, SEEK_END);//char* ch = fgetc(pf);//printf("%c", ch);//fseek(pf, -2, SEEK_END);//ch = fgetc(pf);//printf("%c", ch);fseek(pf, 0, SEEK_SET);int ch = fgetc(pf);printf("%c", ch);fseek(pf, 1, SEEK_SET);ch = fgetc(pf);printf("%c", ch);fseek(pf, 2, SEEK_SET);ch = fgetc(pf);printf("%c", ch);fseek(pf, 3, SEEK_SET);ch = fgetc(pf);printf("%c", ch);fclose(pf);return 0;
}
(2)ftell
Gets the current position of a file pointer.
//获取文件指针的当前位置。
long ftell( FILE *stream );
Return Value
ftell returns the current file position. The value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return–linefeed translation. Use ftell with fseek to return to file locations correctly. On error, ftell returns –1L and errno is set to one of two constants, defined in ERRNO.H. The EBADF constant means the stream argument is not a valid file-handle value or does not refer to an open file. EINVAL means an invalid stream argument was passed to the function. On devices incapable of seeking (such as terminals and printers), or when stream does not refer to an open file, the return value is undefined.
/******************************************************************************/
返回值
ftell返回当前文件位置。由ftell返回的值可能不反映以文本模式打开的流的物理字节偏移量,因为文本模式会引起回车-换行符转换。要正确返回文件位置,请使用ftell和fseek。发生错误时,ftell返回-1L,并将errno设置为ERRNO.H中定义的两个常量之一。EBADF常量表示流参数不是有效的文件句柄值或者不引用一个打开的文件。EINVAL表示将无效的流参数传递给函数。在无法寻找的设备上(如终端和打印机),或者当流不引用一个打开的文件时,返回值是未定义的。
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("The file fails:");return 1;}fseek(pf, 0, SEEK_SET);int ch = getc(pf);int size = ftell(pf);printf("%d\n", size);fclose(pf);pf = NULL;return 0;
}
(3)reward
Repositions the file pointer to the beginning of a file.
//将文件指针重新定位到文件的开头。
void rewind( FILE *stream );
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL) {perror("The file fail:");return 1;}fseek(pf, 2, SEEK_SET);rewind(pf);int size = ftell(pf);printf("%d\n", size);fclose(pf);pf = NULL;return 0;
}
九、 文件读取结束的判定
被错误使⽤的 feof
牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。
- ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL . - ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
例如:
• fread判断返回值是否⼩于实际要读的个数
int main()
{int c;FILE* pf = fopen("test.txt", "r");if (!pf){perror("File opening failed");return EXIT_FAILURE;}//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOFwhile ((c = fgetc(pf)) != EOF) // 标准C I/O读取⽂件循环{putchar(c);}printf("\n");if (ferror(pf)){puts("I/O error when reading");}else if(feof(pf)){puts("End of file reached successfully");}fclose(pf);pf = NULL;return 0;
}
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{double a[SIZE] = {1.,2.,3.,4.,5.};FILE *fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);putchar('\n');} else { // error handlingif (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
}
十、 文件缓冲区(重要)
ANSIC标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。
这个概念很重要,什么是文件缓冲区,它的作用是什么?文件缓冲区就是为文件开辟的内存,它相当于一个缓冲区域,当内存与外设进行数据交互时,例如,把内存中的数据发送到外设(文件)中,数据首先会存储到输出缓冲区,当输出缓冲器装满时,才会把数据发送到外设(文件),从外设到内存是一样的过程!!!
******验证数据缓冲区
int main()
{FILE* pf = fopen("test.txt", "w");if (!pf){perror("File opening failed");return EXIT_FAILURE;}fputs("abcdef", pf);//先将代码放在输出缓冲区printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)//注:fflush 在⾼版本的VS上不能使⽤了printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");Sleep(10000);fclose(pf);//注:fclose在关闭⽂件的时候,也会刷新缓冲区pf = NULL;return 0;
}
完结!!!