文章目录
1 文件
fopen()
第1个参数为文件名,第二个参数为创建的文件开启的权限,返回 FILE*
指针,一般与 fclose()
配合使用,fclose()传入fopen()返回的文件指针。
fopen()返回的文件指针并不指向实际的文件,它指向一个包含文件信息的数据对象,其中包含操作文件的I/O函数所用的缓冲区信息。
fclose()返回整数值,返回0表示关闭成功,否则返回EOF失败。
#include<stdio.h>
#include<stdlib.h>
// argc表示输入参数
// argv表示参数数据,argv[0]为终端输入的命令
int main(int argc, char *argv[]) {
int ch;
FILE *fp; // 文件指针
unsigned long count = 0;
if (argc != 2) {
printf("Usage:%s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// fopen()创建一个文件
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
// 将输入写入文件,读取到文件结束符EOF=-1则结束
while ((ch = getc(fp)) != EOF) {
putc(ch, stdout);
count++;
}
// 关闭文件释放内存
fclose(fp);
return 0;
}
2 getc()和putc()
getc()
和 putc()
函数与 getchar()
与 putchar()
函数类似。所不同的是getc()和putc()要告诉函数使用哪一个文件。
char ch;
FILE *fp = fopen("test.txt", "r");
// 获取标注输入字符
ch = getchar();
ch = getc(fp);
putc(ch, fpout);
fclose(fp);
3 文件拷贝压缩
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define LEN 40
int main(int argc, char *argv[]) {
FILE *in, *out;
int ch;
char name[LEN];
int count = 0;
// 检查终端输入参数
if (argc < 2) {
fprintf(stderr, "Usage:%s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((in = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "I couldn't open the file");
exit(EXIT_FAILURE);
}
strncpy(name, argv[1], LEN - 5); // 拷贝文件名
name[LEN - 5] = '\0'; // 设置空字符
strcat(name, ".red"); // 拼接字符串,文件名后添加.red
if ((out = fopen(name, "w)) == NULL) {
fprintf(stderr, "Can't create output file");
exit(3);
}
// 拷贝数据
while ((ch = getc(in)) != EOF) {
if (count++ % 3 == 0) {
putc(ch, out); // 打印3个字符中的第1个字符
}
}
// 关闭文件
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr, "Error in cloing files");
return 0;
}
4 fprintf()和fscanf()
fprintf()
和 scanf()
函数与 printf()
和 sanf()
类似,区别在于前者需要用第1个参数指定待处理的文件。
rewind()
函数传入一个文件指针,将文件光标返回到开始位置,方便从头开始读取文件。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 41
int main(void) {
FILE *fp;
char words[MAX];
if ((fp = fopen("wordy", "a+") == NULL) {
fprintf(stdout, "Can't open file");
exit(EXIT_FAILURE);
}
puts("Enter words to add to the file; press the #");
puts("key at the beginning of a line to terminate");
// 将终端输入写入文件,输入'#'结束
while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#')) {
fprintf(fp, "%s", words);
}
puts("File contents:");
rewind(fp); // 返回文件开始处
while (fscanf(fp, "%s", words) == 1) {
puts(words);
}
if (fclose(fp) != 0) {
fprintf(stderr, "Error closing file");
}
return 0;
}
5 fgets()和fputs()
fgets(buf, STLEN, fp);
buf
是char类型数组的名称,STLEN
是字符串的大小,fp
文件指针。
fgets()
函数读取输入直到第1个换行符的后面或读到文件结尾,或者读取STLEN-1个字符。fgets()在末尾添加一个空字符使之成为一个字符串,字符串的大小是其字符加上一个空字符。
如果fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符放在空字符前面。
fgets()函数在遇到EOF时将返回NULL,可以利用这一机制检查是否到达文件结尾;如果未遇到EOF则返回传给它的地址。
fputs(buf, fp);
buf
是字符串的地址,fp
用于指定目标文件。
由于fgets()保留了换行符,fputs()就不会再添加换行符。
6 fseek()和ftell()
有了 fseek()
函数,便可以把文件看作是数组,在fopen()打开的文件中直接移动到任意字节处。fseek()返回int类型的值。
ftell()
函数返回一个long类型的值,表示文件中的当前位置。
// 倒叙显示文件的内容
#include<stdio.h>
#include<stdlib.h>
#define CNTL_Z '\032'
#define SLEN 81
int main(void) {
char file[SLEN];
char ch;
FILE *fp;
long count, last;
puts("Enter the name of the file to be proccessed");
scanf("%80s", file);
if ((fp = fopen(file, "rb")) == NULL) {
printf("reverse can't open file");
exit(EXIT_FAILURE);
}
// 第1个参数:FILE*文件指针
// 第2个参数:偏移量,表示从起始点要移动的距离,必须是long类型,可以为正(前移)、负(后移)或0(保持不动)
// 第3个参数:模式,有三种模式:
// 1、SEEK_SET:文件开始处
// 2、SEEK_CUR:当前位置
// 3、SEEK_END:文件末尾
fseek(fp, 0L, SEEK_END); // 定位到文件末尾
last = ftell(fp); // 把从文件开始处到文件结尾的字节数赋给last
for (count = 1L; count <= last, count++) {
fseek(fp, -count, SEEK_END); // 回退
ch = getc(fp);
// MS-DOS文件
if (ch != CNTL_Z && ch != '\r') {
putchar(ch);
}
}
putchar('\n');
fclose(fp);
return 0;
}
7 fgetpos()和fsetpos()
fseek()
和 ftell()
潜在问题是,它们都把文件大小限制在long类型能表示的范围内。ANSI C新增了两个处理较大文件的新定位函数:fgetpos()
和 fsetpos()
。
这两个函数不使用long类型的值表示位置,使用一种新类型:fpos_t类型。fpos_t类型的变量或数据对象可以在文件中指定一个位置,它不能是数组类型。
// 调用该函数时,fpos_t类型的值放在pos指向的位置上,该值描述了文件中的一个位置。如果成功,fgetpos()函数返回0;如果失败,返回非0
int fgetpos(FILE* restrict stream, fpos_t * restrict pos);
// 调用该函数时,使用pos指向位置上的fpos_t类型值来设置文件指针指向该值指定的位置。如果成功,fsetpos()函数返回0,否则返回非0
int fsetpos(FILE *stream, const fpos_t *pos);
8 ungetc()
int ungetc(int c, FILE *fp)
函数把c指定的字符放回输入流中。如果把一个字符放回输入流,下次调用标准输入函数时将读取该字符。
9 fflush()
int fflush(FILE *fp);
调用 fflush()
函数引起输出缓冲区中所有的未写入数据被发送到 fp
指定的输出文件。如果 fp
是空指针,所有输出缓冲区都被刷新。在输入流中使用fflush()函数的效果是未定义的。只要最近一次操作不是输入操作,就可以用该函数来更新流。
10 setvbuf()
int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size);
setvbuf()
函数创建了一个供标准I/O函数替换使用的缓冲区。在打开文件后且未对流进行其他操作之前,调用该函数。
指针 fp
识别待处理的流;
buf
指向待使用的存储区,如果buf的值不是NULL,则必须创建一个缓冲区;
mode
设置缓冲区的模式:
-
_IOFBF表示完全缓冲(在缓冲区满时刷新)
-
_IOLBF表示行缓冲(在缓冲区满时或写入一个换行符时)
-
_IONBF表示无缓冲
如果操作成功,函数返回0,否则返回一个非0值。
11 fread()和fwrite()
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);
fwrite()
函数把二进制数据写入文件。
指针 ptr
是待写入数据块的地址,size
表示待写入数据块的大小(以字节为单位),nmemb
表示待写入数据块的数量。
// 把一块256字节的数据从buffer写入文件
char buffer[256];
fwrite(buffer, 256, 1, fp);
// 把earnings数组中的数据写入文件
double earnings[10];
fwrite(earnings, sizeof(double), 10, fp);
size_t fread(void * resttict ptr, size_t size, size_t nmemb, FILE * restrict fp);
指针ptr表示待读取文件数据在内存中的地址。
// 从earnings数组读取数据,如果读取正常则返回值就是nmemb
// 如果读取错误或到文件结尾,返回值比nmemb小
double earnings[10];
fread(earnings, sizeof(double), 10, fp);
12 feof()和ferror()
如果标准输入函数返回EOF,则通常表明函数已到达文件结尾;然而,出现读取错误时,函数也会返回EOF。
feof()
和 ferror()
用于区分这两种情况。
当上一次输入调用检测到文件结尾时,feof()返回一个非0值,否则返回0。
当读或写出现错误,ferror()返回一个非0值,否则返回0。
// 把文件附加到另一个文件末尾
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *source, FILE *dest);
char * s_gets(char * st, int n);
int main(void) {
FILE *fa, *fs; // fa指向目标文件,fs指向源文件
int files = 0; // 附加的文件数量
char file_app{SLEN]; // 目标文件名
int ch;
puts("Enter name of destination file");
s_gets(file_app, SLEN);
if ((fa = fopen(file_app, "a+")) == NULL) {
fprintf(stderr, "Can't open file %s", file_app);
exit(EXIT_FAILURE);
}
// 创建输出缓冲区
if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0) {
fputs("Can't create output buffer");
exit(EXIT_FAIULRE);
}
puts("Enter name of first source file (empty line to quit)");
while (s_gets(file_src, SLEN) && file_src[0] != '\0') {
// 写入文件和读取文件名不是同一个文件
if (strcmp(file_src, file_app) == 0) {
fputs("Can't append file to itself");
} else if ((fs = fopen(file_src, "r")) == NULL) {
fpritnf(stderr, "Can't open %s", file_src);
} else {
// 创建输入缓冲区
if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0) {
fputs("Can't create input buffer");
continue;
}
// 拼接文件内容
append(fs, fa);
// 检查是否读取正常
if (ferror(fs) != 0) {
fprintf(stderr, "Error in reading file %s", file_src);
}
if (ferror(fa) != 0) {
fprintf(stderr, "Error in writing file %s", file_app);
}
fclose(fs);
files++;
printf("File %s appended", file_src);
}
}
printf("Done appending. %d files appended", files);
rewind(fa);
while ((ch = getc(fa)) != EOF) {
putchar(ch);
}
fclose(fa);
return 0;
}
void append(FILE *source, FILE *dest) {
size_t bytes;
static char temp[BUFSIZE]; // 只分配一次
while ((bytes = fread(temp, sizeof(char), BUFSIZE, source)) > 0) {
fwrite(temp, sizeof(char), bytes, dest);
}
}
char * s_gets(char *st, int n) {
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if (ret_val) {
find = strchr(st, '\n');// 查找换行符
if (find) // 如果地址不是NULL
*find = '\0'; // 放置一个空字符
else
while (getchar() != '\n')
continue;
}
return ret_val;
}
// 二进制I/O进行随机访问
#include<stdio.h>
#include<stdlib.h>
#define ARSIZE 1000
int main() {
double numbers[ARSIZE];
double value;
const char * file = "numbers.dat";
int i;
long pos;
FILE *iofile;
// 创建一组double类型的值
for (i = 0; i < ARSIZE; i++) {
numbers[i] = 100.0 * i + 1.0 / (i + 1);
}
if ((iofile = fopen(file, "wb")) == NULL) {
exit(EXIT_FAILURE);
}
// 以二进制格式把数组写入文件
fwrite(numbers, sizeof(double), ARSIZE, iofile);
fclose(iofile);
if ((iofile = fopen(file, "rb")) == NULL) {
exit(EXIT_FAIULRE);
}
// 从文件中读取选定的内容
while (scanf("%d", &i) == 1 && i >= 0 && i < ARSIZE) {
pos = (long) i * sizeof(double); // 计算偏移量
fseek(iofile, pos, SEEK_SET); // 定位到此处
fread(&value, sizeof(double), 1, iofile);
}
fclose(iofile);
return 0;
}