一、文件
1、文件
文件通常是磁盘或固态硬盘上面的一段已命名的储存区。所有文件的内容都是以二进制形式(0或1)储存。
2、文本模式和二进制模式
文本文件:文件最初使用二进制编码(ASCII或者Unicode)的字符表示文本,改文件就是文本文件,其中包含文本内容。
二进制文件:如果文件中的二进制代表机器语言或者数值数据(使用相同的内部表示,假设,用long或者double类型的值)或图片或音乐编码,改文件就是二进制文件,其中包含二进制文件。
C语言提供俩种访问文件的途径:二进制模式和文本模式。二进制模式中,程序可以访问文件的每个字节。在文本模式中门程序所见的内容和文件的实际内容不同。
3、标准文件
C语言会自动打开三个文件,他们被称为标准输入、标准输出和标准错误输出。在默认情况下,标准输入是系统的普通输入设备,如键盘;标准输出和标准错误输出系统的普通输出设备,如显示器。
二、标准I/O
1、fopen函数
extern FILE *fopen( const char *__filename, const char *__type );
fopen的第一个参数是待打开文件的名称,第二个参数指定打开文件的模式。
模式字符串 | 含义 |
---|---|
“r” | 以只读模式打开文件 |
“w” | 以写模式打开,把现有文件的长度截为0,如果文件不存在,则创建一个新文件 |
“a” | 以写模式打开,在现有文件末尾添加内容,如果文件不存在,则创建一个新文件 |
“r+” | 以更新模式打开文件(即可以读写文件) |
“w+” | 以更新模式打开文件(即,读和写),把现有文件的长度截为0,如果文件不存在,则创建一个新文件 |
“a+” | 以更新模式打开文件(即,读和写),在现有文件末尾添加内容,如果文件不存在,则创建一个新文件;可以读整个文件,但是只能从末尾添加内容 |
“rb”、“wb” 、“ab” 、 “rb+”、“r+b”、“wb+”、 “w+b”、“ab+”、 “a+b” | 与上一个模式相同,但是以二进制打开文件 |
2、getc()和putc()函数
ch=getc(fp);
上面的语句表示从fp指定的文件中获取一个字符。
putc(ch, fpout);
上面的语句是把字符ch放入到FILE指针fpot指定的文件中。
3、文件结尾
如果getc()函数在读取一个字符发现是文件结尾,它将返回一个特殊值EOF。所以C程序只有在读到超过文件末尾时才会发现文件的结尾。
4、fclose()函数
fclose(fp)函数关闭指定的文件,必要时刷新缓冲区。如果成功关闭,fclose()函数返回0,否则返回EOF。
5、示例
/*
* 压缩文件为原来的三分之一
*/
#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(1);
}
//fprintf(stderr, "11111111111112. \n ");
// 实现输入
if( (in = fopen( argv[1] , "r" ) ) == NULL )
{
fprintf(stderr, "I couldn't open the file \"%s\" \n ", argv[1]);
exit(2);
}
// 实现输出
if ( (out = fopen(name, "w") ) == NULL ) // 打开文件以供写入
{
fprintf(stderr, "Can't create output file . \n ");
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 closing file . \n ");
}
return 0;
}
三、文件I/O:fprintf()、fscanf()、fgets()和fputs
1、fprintf()、fscanf()
/* 使用fprintf()、fscanf()和rewind() */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 40
int main()
{
FILE *fp;
char words[MAX];
if ((fp = fopen("wordy", "a+")) == NULL )
{
fprintf(stdout, "Can't open \"wordy\" file \n");
exit(EXIT_FAILURE);
}
puts("请输入添加到文件中的单词,终止键#结束\n");
puts("在一行的开头键入终止键\n");
while((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#'))
{
fprintf(fp, "%s\n", words);
}
puts("文件内容:");
rewind(fp); // 返回文件开始处
while(fscanf(fp, "%s", words) == 1)
{
puts(words);
}
puts("完成!");
if(fclose(fp) != 0)
{
fprintf(stderr, "ERR CLOSE FILE \n");
}
return 0;
}
2、fgets()和fputs()
fgets(buf, STLEN, fp);
buf是char类型数组的名称,STLEN是字符串的大小,fp是指向FILE的指针。
fgets()函数读取到第一个换行符的后面,或者读到文件结尾,或者读取到STLEN-1个字符。然后fgets在末尾添加一个空字符使之成为一个空字符串。如果 fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符放在空字符前面。fgets()函数在遇到EOF时将返回NULL值,可以利用这一机制检查是否到达文件结尾。
fputs(buf, fp);
fputs()接受俩个参数,第一个是字符串的地址;第二个是文件指针。该函数根据传入地址找到的字符串写入指定文件。和puts()函数不同,fputs在打印字符串时不会在其末尾添加换行符。
四、随机访问:fseek()和ftell()
#include <stdio.h>
#include <stdlib.h>
#define SLEN 81
int main()
{
char file[SLEN];
char ch;
FILE *fp;
long count = 0;
long last = 0;
puts("请输入你的文件名:");
scanf("%80s", file);
if( (fp = fopen(file, "rb")) == NULL )
{
printf("文件不能打开[%s]\n", file);
}
// 定位到文件末尾
fseek(fp, 0L, SEEK_END);
last = ftell(fp);
for(count = 1L; count <= last; count++)
{
fseek(fp, -count, SEEK_END);
ch = getc(fp);
if(ch != EOF && ch != '\n')
{
putchar(ch);
}
}
putchar('\n');
fclose(fp);
return 0;
}
1、fseek()和ftell()
fseek()的第一个参数是FILE指针,指向待查找的文件,fopen()应该已打开改文件;第二个参数是偏移量,该参数表示从起始点开始要偏移的距离,必须是long类型的值,可以为正(前移)、负(后移)、或0(保持不动);第三个参数是模式,该参数确定起始点。根据ANSI标准,在stdio.h规定了几个表示模式的明示变量。
模式 | 偏移量的起始点 |
---|---|
SEEK_SET | 文件开始处 |
SEEK_CUR | 当前位置 |
SEEK_END | 文件末尾 |
fseek()成功的返回值为0,失败返回-1。
ftell()函数的返回类型是long,它返回的是当前位置,ANSI标准把它定义在stdio.h中。
2、fgetpos()和fsetpos()函数
int fgetspos(FILE * restrict stream, fpost_t * restrict ps);
把fpos_t类型的值放在pos指向的位置上,成功返回0,失败返回非0。
int fsetpos(FILE *stream, const fpos_t *pos);
使用pst指向位置上的fpos_t类型值来设置文件指针指向该指定的位置。成功返回0,失败返回非0。
五、其他标准I/O函数
1、int ungetsc(int c, FILE *fp)
int ungetsc(int c, FILE *fp)把c指定的字符放回到输入流中。
2、int fflush()函数
int fflush(FILE *fp);
调用fflush()函数引起输出缓冲区中所有未写入数据被发送到fp指定的输出文件。这个过程称为刷新缓冲区。如果fp是空指针,所有输出缓冲区都要被刷新。在输入流中使用fflush()函数的效果是未定义的。只要最近一次操作不是输入操作,就可以用该函数来更新流(任何读写模式)。
3、int setvbuf()函数
int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size);
setvbuf()函数创建了一个供I/O函数替换使用的缓冲区。再打开文件后且未对文件流进行其他操作之前,调用该函数。指针fp识别待处理的流,buf指向待使用的储存区。如果buff的值不是NULL。则必须创建一个缓冲区。变量size告诉setvbuf()数组的大小。mode的选择如下:_IOFBF表示完全缓冲(在缓冲区满时刷新);_IOLBF表示行缓冲(在缓冲区满时写入一个换行符时);_INOBF表示无缓冲。如果成功,返回0,否则返回非0值。
4、二进制I/O :fread()和fwrite()
操作二进制数据
5、size_t fwrite()
函数
size_t fwrite(const void *restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);
fwrite()函数把二进制数据文件写入文件。把数据写入文件。
char buffer[256];
fwrite(buffer, 256, 1, fp);
fwrite()函数返回成功写入项的数量,成功返回nmemb,如果写入错误,返回的值比nmemb小。
6、size_t fread()
函数
size_t fread(const void *restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);
ptr是待读取文件数据在内存中的地址,fp待读取的文件。
// 读取保存的内含10个double类型值的数组
double ear[10];
fread(ear, 10, 10, fp);
fread()函数返回成功读取的数量,成功返回nmemb,如果读取错误或读到文件结尾,返回的值比nmemb小。
7、int feof(FILE *fp)和int ferror(FILE *fp)函数
如果标准输入函数返回EOF,则通常表明函数已到达文件结尾。然而读取错误时,函数也会返回EOF, feof(FILE *fp)和ferror(FILE *fp)函数用于区分这俩种情况。当上一次输入调用检测文件结尾时,feof()函数返回一个非0值,否则返回0,。当读写出现错误,ferror()函数返回一个非0值,否则返回0。
8、把文件附加到另一个文件结尾
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *soure, FILE *dest);
char * s_gets(char * st, int n);
int main()
{
FILE *fa; // 目标文件
FILE *fs; // 源文件
char file_app[SLEN]; // 目标文件名
char file_src[SLEN]; // 源文件名
int files = 0; // 附加的文件数量
int ch = 0;
puts("请输入目标文件:");
s_gets(file_app, SLEN);
if((fa =fopen(file_app, "a+")) == NULL )
{
fprintf(stderr, "打开文件[%s]失败!!\n",file_app);
exit(EXIT_FAILURE);
}
if(setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("创建缓冲区失败!!\n", stderr);
exit(EXIT_FAILURE);
}
puts("请输入源文件:");
while (s_gets(file_src, SLEN) && file_src[0] != '\0')
{
if(strcmp(file_src, file_app) == 0)
{
fputs("创建缓冲区失败!!\n", stderr);
}
else if((fs=fopen(file_src, "r")) == NULL)
{
fprintf(stderr, "打开文件[%s]失败!!\n", file_src);
}
else
{
if(setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("创建缓冲区失败!!\n", stderr);
continue;
}
append(fs, fa);
if(ferror(fs) != 0)
{
fprintf(stderr, "ERROR in 读取文件[%s]!!\n",file_src);
}
if(ferror(fa) != 0)
{
fprintf(stderr, "ERROR in 写入文件[%s]!!\n",file_app);
}
fclose(fs);
files++;
printf("文件[%s]追加完成", file_src);
puts("下一个文件(空行退出)");
}
}
printf("文件追加结束。[%d]文件追加\n", files);
rewind(fa); // 返回文件开始处
printf("[%s]文件内容:\n", file_app);
while ((ch = getc(fa)) != EOF)
{
putchar(ch);
}
puts("结束");
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)
{
*find = '\0';
}
else
{
while(getchar() != '\n')
{
continue;
}
}
}
return ret_val;
}