与文件进行通信
文本模式和二进制模式
c提供两种访问文件的途径:二进制模式和文本模式。二进制模式下可访问文件的每个字节。
MS-DOS模式下, 用\r\n表示新的一行, Ctrl+z表示文件的结束。
I / O级别
底层I/O(low-level I/O)和标准高级I/O(standard high-level I/O).
c标准只支持标准I/O包, 但是c标准建立了可移植的I/O模型。
标准文件
c程序自动会打开三个文件:标准输入(standard input), 标准输出(standard output), 标准错误输出(standard error output)。
重定向是把其他文件视为标准输入或标准输出。
标准错误输出始终会向屏幕上发送。
标准I/O
标准I/O输入和输出都是缓冲的。极大提高了传输效率。
#include <stdio.h>
#include <stdlib.h>
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);
}
if ((fp = fopen(argv[1], "r") == NULL))
{
printf("Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
while (ch = getc(fp) != EOF)
{
putc(ch, stdout);
count++;
}
fclose(fp);
printf("File %s has %lu characters\n", argv[1], count);
return 0;
}
检查命令行参数
以上通过检查argc的值检查是否存在命令行参数。
tips:有些系统不识别argv[0]
exit()关闭所有打开的文件并结束程序。
c标准要求0或EXIT_SUCCESS表明成功退出程序。
EXIT_FAILURE表明结束程序失败。
以上的宏和exit()定义在stdlib.h中。
exit()会直接终止程序, return 若在递归程序中, 只会把控制权交给上一级递归。
fopen() 函数
fopen()函数第一个参数是代打开文件的名称(包含该文件名的字符串地址),第二个参数是一个字符串, 表示待打开文件的模式。
“r” ——————读模式
“w”——————写模式, 现有文件长度截为0
“a”——————写模式, 现有文件后面追加
“r+”——————更新模式(读写)
“w+”——————更新模式, 现有文件截为0
“a+” ——————更新模式, 现有文件后面追加
“rb”, “wb”, “ab”, “rb+”, “r+b”, “wb+”, “w+b”, “ab+”, “a+b”——二进制模式
“wx”, “wbx”, “w+x”, “wb+x”, “w + bx”————若文件已存在或以独占模式打开文件, 则打开文件失败
UNIX, Linux只有一种文件模式, 带b不带b模式相同。
fopen()返回文件指针(file pointer), 类型是指向FILE的指针。
getc()和putc()函数
getc()与getchar()类似, 其参数为指定的文件的文件指针。
putc()第一个文件是待写入的字符, 第二个参数是文件指针。
stdout是与标准输出相关联的文件指针, 定义在stdio.h中
putc(ch, stdout)与putchar(ch)作用相同。
文件结尾
int ch;
FILE *fp;
fp = fopen("wacky.txt", "r");
while ((ch = getc(fp)) != EOF)
putchar(ch);
getc()在读到文件结尾时, 将返回特殊值EOF
fclose() 函数
fclose()关闭指定的文件, 必要时刷新缓冲区, 成功返回0, 否则返回EOF
if (fclose(fp) != 0)
printf("Error");
磁盘已满, 移动硬盘被移除, I/O错误都会导致该函数失败
一个简单的文件压缩程序
#include <stdio.h>
#include <string.h>
#include <stdlib.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 could not open the file]\"%s\"\n", argv[1]);
exit(EXIT_FAILURE);
}
strncpy(name, argv[1], LEN - 5);
name[LEN - 5] = '\0';
stcat(name, ".red");
if ((out = fopen(name, "w") == NULL))
{
fprintf(stderr, "Can not creat output file.\n");
exit(3);
}
while ((ch = getc(in)) != EOF)
if (count++ % 3 == 0)
putc(ch, out);
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr, "Error in closing files\n");
return 0;
}
fprintf和printf()类似, 但是其第一个参数必须是文件指针, stderr指针把错误信息发送至标准错误输出。
可以同时打开的文件数量有限, 大约为10 - 20个。
文件I/O:fprintf(), fscanf(), fgets(), fputs()
fprintf()和fscanf()函数
#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 not open \"wordy\" file.\n");
exit(EXIT_FAILURE);
}
puts("Enter words to add to the file; press the # key at the beginning of a line to terminate.");
while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#'))
fprintf(fp, "%s\n", words);
puts("File contents:");
rewind(fp); //指针回到原来位置
while (fscanf(fp, "%s", words) == 1)
puts(words);
puts("done.");
if (fclose(fp) != 0)
fprintf(stderr, "Error closing file\n");
return 0;
}
a+模式以更新模式打开文件, 在文件末尾添加内容
rewind()让程序回到文件起始的位置, rewind()接受一个文件指针作为参数。
fprint(), fscanf()都把FILE指针作为第一个参数。
fgets() & fputs()函数
fgets()函数第一个参数是表示储存输入位置的地址, 第二个参数是表示待输入字符串大小的整数, 第三个参数是文件指针, 指定待读取文件。
fgets(buf, STLEN, fp);
fgets()读取到第一个换行符的后面, 文件结尾, 或STLEN-1个字符。
fgets()遇到EOF时返回NULL,否则返回它的第一个参数地址。
fputs()接受的第一个参数地址是字符串的地址, 第二个是待写入文件的文件指针。
随机访问: fseek()和ftell()
#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 processed:");
scanf("%80s", file);
if ((fp = fopen(file, "rb")) == NULL) // 二进制读模式
{
printf("reverse can not open %s\n", file);
exit(EXIT_FAILURE);
}
fseek(fp, 0L, SEEK_END);
last = ftell(fp);
for (count = 1L; count <= last; count++)
{
fseek(fp, -count, SEEK_END);
ch = getc(fp);
if (ch != CNTL_Z && ch != '\r')
putchar(ch);
}
putchar('\n');
fclose(fp);
return 0;
}
fseek()第一个参数是FILE指针, 第二个参数是偏移量(offset), 必须是long类型的值;第三个参数是模式, 确定起始点,根据ANSI标准, 声明在stdio.h中
SEEK_SET——————文件开始处
SEEK_CUR——————当前位置
SEEK_END——————文件结尾
fseek()正常返回值是1,错误时返回-1;
ftell()函数接受文件指针作为参数, 返回long型指向该文件当前位置距离文件开始处的字节数来确定文件位置。文件第一个字节到文件开始的距离为0。ANSI C规定该定义适用于以二进制模式打开的文件。
二进制模式和文本模式
UNIX只有一种文件格式, 不需要特殊的转换
MS-DOS使用Ctrl+Z标记文本文件的结尾, 二进制模式打开时被看作是一个字符, 实际的文件结尾在该字符之后, 可能紧跟在其后, 也可能用空字符填充, 使文件大小恰好为256的倍数,在DOS环境下不会打印空字符。
MS-DOS用]\r\n表示文本文件换行, 以文本模式打开时将其看作‘\n’, 二进制模式下程序可看见这两个字符。
ANSI C规定, 对于文本模式, ftell返回的值可作为fseek()的第二个参数, 对于MS-DOS, ftell返回值将\r\n看作一个字节计数。
可移植性
ANSI对这些函数降低了要求,
二进制模式下, 部分编译器可能不支持SEED_END模式。
在文本模式下, 只有以下调用可保证其相应的行为:
fseek(file, 0L, SEEK_SET)
fseek(file, 0L, SEEK_CUR)
fseek(file, 0L, SEEK_END)
fseek(file, ftell-pos, SEEK_SET)
fgetpos() & fsetpos() 函数
fseek()和ftell()他们都把文件大小限制在long类型范围内
ANSI C 新赠了处理较大文件的新定位函数:fgetpos()和fsetpos()。其使用fpos_t类型(file position type, 文件定位类型), fpos_t并非基本类型, 而是根据其他类型来定义,fpos_t类型的变量或数据对象可在文件中指定一个非数组型的位置, 没有其他限制
int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
该函数把文件中当前位置距文件开头的字节数放在pos指向的位置上, 成功则返回0, 否则返回非0;
int fsetpos(FILE * stream, const fpos_t * pos);
该函数使用pos指定位置上的fpos类型值来设置文件指针偏移该值后指定的位置(在文件开始处进行偏移), 成功返回0, 否则返回非0, fpos_t的值可通过调用fgetpos()获得。