目录
一、错误报告函数
二、终止执行
三、I/O概念
四、打开流 / 关闭流
五、字符I/O
1、getchar / putchar
2、getc / putc
3、fgetc / fputc
六、未格式化的行 I/O
1、gets / puts
2、fgets / fputs
七、格式化I/O
1、scanf / printf
2、fscanf / fprintf
八、二进制I/O
1、fread / fwrite
定义:void perror(const char* str);
注:如果perror函数的参数str指针不是NULL,并指向一个非空的字符串,perror 函数先打印这个字符串,然后跟着打印一个冒号和一个空格,接着输出错误信息提示。
通过代码来理解函数:
int main()
{
FILE* pFile = fopen("unexist.ent", "rb");
if (pFile == NULL)
{
perror("The following error occurred");
}
else
{
fclose(pFile);
}
return 0;
}
运行结果:
定义:void exit (int status);
注:参数 status 返回给了操作系统,用于提示程序是否正常完成。
C语言定义了预定义符号:EXIT_SUCCESS 和 EXIT_FAILURE 来表示成功或者失败返回。
通过代码来理解函数:
int main()
{
FILE* pFile = fopen("unexist.ent", "rb");
if (pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
else
{
fclose(pFile);
}
return 0;
}
程序运行完 perror 函数之后直接返回。
流:就 C 程序而言,所有的 I/O 操作就是简单的从程序移进、移出字节的事情,因此这种字节流就被称为流。
分类:文本流 和二进制流
-- 文本流在不同的系统中实现不太相同。
-- 二进制流中的字节完全是安装程序编写它们形式写入到文件和设备中。完全根据它们从文件或设备读取的形式读入到程序。
· 对于每一个 ANSI C 程序默认打开三个流:标准输入(stdin)、标准输出(stdout)、标准错误(stderr)。它们都是一个指向 FILE 结构的指针。(FILE 是一个结构体类型,用于访问一个流)
I/O 常量:EOF -- 文件结束标志,表示文件到了结尾。
EOPEN_MAX -- 一个程序最多打开文件数。
FILENAME_MAX -- 文件名的最大长度。
I/O函数概括:
-- 打开流:fopen 函数 ,打开流时必须指定需要访问的文件或者设备已经访问的方式。fopen 函数和操作系统去验证文件或者设备确实存在,验证访问访问方式,然后初始化 FILE 结构。
-- 关闭流:fclose 函数 , 关闭一个流可以防止与它相关的文件被再次访问,保证任何存储于缓冲区中的数据被正确的写入文件中。
I/O 函数的三种方式:单个字符、文本行、二进制数据。
定义:FILE* fopen (const char* filename, const char* mode);
int fclose (FILE* stream);
代码演示:
int main()
{
FILE* pFile = fopen("myfile.txt", "w");
if (pFile != NULL)
{
fputs("example", pFile);
fclose(pFile);
}
return 0;
}
运行之后,我们会发现文件中多了一个 “myfile.txt” 的文件,打开后发现文件中显示 “example‘,如下图:
这表示我们是的确打开了一个文件,并且关闭了。
下面我们要看一下,如果一个一直打开文件不关闭,是否会死循环,代码为:
int main()
{
int count = 0;
FILE* pFile;
while (1)
{
pFile = fopen("myfile.txt", "r");
if (pFile == NULL)
{
perror("fopen :");
printf("count = %d\n", count);
system("pause");
return;
}
++count;
}
system("pause");
return 0;
}
运行结果:
结果显示表明一个程序最多打开 509 个文件,其实这句话是错误的,一个程序员怎么会设计一个 509 这样的数字,总感觉怎么也应该是 512 个比较正常,这就要说我们之前提到的,一个程序会默认打开三个流:标准输入(stdin)、标准输出(stdout)、标准错误(stderr),加起来就是512。那么如果我们说一个程序最多打开 512 个文件,其实也是错的,这也是要看我们的程序所处平台等因素,在这里我们用的是win32位平台。
定义:int gethcar (void); //从标准输入流获取一个字符
int putchar (int character); //从标准输出输出流输出一个字符
:
int main()
{
int c;
//puts可以用于输出一行文本到标准输出流
puts("Enter text.Include a dot ('.')in a sentence to exit");
do
{
//getchar是从标准输入流获取一个字符
c = getchar();
//putchar是向标准输出流输出一个字符
putchar(c);
} while (c != '.');
return 0;
}
运行结果:
定义:int getc ( FILE* stream); //读文件 stream 中的一个字符
int putc ( int character , FILE* stream); //向文件 stream 输入一个字符
代码演示:
int main()
{
FILE* pFile;
char c;
int n = 0;
//fputc example
pFile = fopen("alphabet.txt", "wt");
if (pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
for (c = 'A'; c <= 'Z'; c++)
{
fputc(c, pFile); //向文件alphabet.txt输入字符
//循环结束时文件中存的应该是 A B C D E F ... X Y Z
}
fclose(pFile);
//fgetc example
pFile = fopen("alphabet.txt", "r");
if(pFile==NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
else
{
do
{
c = fgetc(pFile);
if (c < 'C')
n++;
} while (c != EOF);
fclose(pFile);
printf("File contains %d$\n", n);
}
system("pause");
return 0;
}
首先我们查看文件 alphabet.txt 的内容:
程序运行结果:
定义:int fgetc (FILE* stream);
int fputc (int character, FILE* stream);
功能:getc 和 fgetc 功能一样 ; putc 和 fputc 功能一样。
定义:char* gets (char* str);
int puts (const char* str);
代码演示:
int main()
{
char str1[256];
char* str2 = "Hello world!";
//gets example
printf("Insert your full address:");
gets(str1); //warning: unsafe (see fgets instead)
printf("Your address is: %s\n", str1);
//puts example
puts(str2);
system("pause");
return 0;
}
运行结果:
定义:char* fgets (char* str, int sum, FILE* stream) ; //从 stream 流中读取 sum 个字符存储到 str 中
int fputs (const char* str, FILE* stream); //向 stream 流中输入一个字符串
代码演示:
int main()
{
FILE* pFile;
char mystring[100];
const char* str = "hello world";
//fputs example
pFile = fopen("alphabet.txt", "r");
if (pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
else
{
if (fgets(mystring, 27, pFile) != NULL)
{
puts(mystring);
}
fclose(pFile);
}
//fputs example
pFile = fopen("alphabet.txt", "a"); //打开并在文件末尾进行写操作
if (pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
fputs(str, pFile);
fclose(pFile);
pFile = fopen("alphabet.txt", "r");
if (pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
else
{
if (fgets(mystring, 38, pFile) != NULL)
{
puts(mystring);
}
fclose(pFile);
}
system("pause");
return 0;
}
运行结果:
注:在之前的演示中,我们向 alphabet.txt 中写入了ABCDEFGHIJKMNOPQRSTOVWXYZ,首先我们将文件中的内容存储到 mystring 中,间接查看文件中的内容,然后应用向文件末尾写的操作,追加一个字符串,再一次间接查看文件内容。
定义:int scanf ( const char* format, ...);
int printf ( const char* format, ...);
注:我们发现它两的定义格式使用了可变参数列表,因此参数个数是不固定的,在这里,我们只来讨论它们的用法,不讨论可变参数列表的使用,若想了解可变参数列表的内容,可点击下方链接:
可变参数列表
代码演示:
int main()
{
char str[80];
int i;
//scanf example
printf("scanf example\n");
printf("Enter your family name:");
scanf("%79s", str); //str 数组名表示首元素地址,79表示最大输入字符数
printf("Enter your age:");
scanf("%d", &i); //必须取地址&,%d表示输入整数
printf("Mr.%s, %d years old.\n", str, i);
printf("Enter a hexadecimal number:");
scanf("%x", &i); //必须取地址,%x表示输入无符号的十六进制整数
printf("You have entered %#x (%d).\n\n", i, i);
//printf example
printf("printf example\n");
printf("Characters: %c %c\n", 'a', 65);//字符输出的两种方式,一种直接传字符,一种传ASCII码值,输出ASCII码值对于字符
printf("Deccimals:%d %ld\n", 1977, 650000L); // l表示用于长整型整数,可加在格式符 d、o、x、u之前
printf("Preceding with blanks:%10d\n", 1977); //10表示数据最小宽度,10为正数,宽度不足左边补空格
printf("Some different radices:%d %x %o %#x %#o \n", 100, 100, 100, 100, 100); /* 同一个整数的不同格式输出
%d:整数 %x:以十六进制无符号形式输出整数 %o:以八进制无符号形式输出整数
%#x:前面加0x,以十六进制无符号形式输出整数 %#o:前面加0x,以八进制无符号形式输出整数 */
printf("floats:%4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); /* 同一个浮点数的不同格式输出
%4.2f:输出数据占4列,其中有2位小数,若数值长度小于4,则左补空格
%+.0e:+后无数字,表示输出为数据的长度6,指数输出为e+00,故前面只有两位,补+3
%E:以指数形式输出整数 */
printf("Width trick:%*d \n", 5, 10); //%*d:表示将要使用的是一个可变的宽度,5代理*,打印10。
printf("%s\n", "A string"); //传字符串
system("pause");
return 0;
}
运行结果:
定义:int fscanf (FILE* stream, const char* format, ...); //从磁盘文件中读入ASCII字符
int fprintf (FILE* stream, const char* format, ...);
注:与scanf() 和printf()的区别在于,fscanf() 和 fprintf() 的读写对象不是终端,而是磁盘文件。
代码演示:
int main()
{
char str[80];
float f;
FILE* pFile;
pFile = fopen("myfile.txt", "w+"); //读写操作
if(pFile == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
fprintf(pFile, "%f,%s", 3.1416, "PI");/* 将3.1416和"PI"以"%f"和"%s"的格式输入到pFile指向的文件中,
文件上的显示为:3.141600,PI */
rewind(pFile); //文件定位函数,将文件的指针重新定位到文件的开头
fscanf(pFile, "%f,%s", &f, str); //将磁盘文件中的3.1416赋给变量f,将"PI"赋给str
fclose(pFile);
printf("I have read: %f and %s \n", f, str);
system("pause");
return 0;
}
运行结果:
建议:使用 fprintf 和 fscanf 函数对磁盘文件读写,使用方便,容易理解,但由于在输入时要将 ASCII 码转换为二进制形式,在输出时又要将二进制形式转换为字符,花费时间比较多。因此,在内存与磁盘频繁交换数据的情况下,最好不用 fprintf 和 fscanf 函数,而用 fread 和 fwrite函数。下面将会说到。
定义:size_t fread ( void* ptr, size_t size, size_t count, FILE* stream); //从一个流中读一个数据
size_t fwrite ( const void* ptr, size_t size, size_t count, FILE* stream); //写一个数据到流里
注:ptr : 是一个指针。对于 fread 来说,它是读入数据的存放地址;对于fwrite 来说,是要输出数据的地址(以上指的都是起始地址)。
size : 要读写的字节数。
count : 要进行读写多少个 size 字节的数据项。
stream : 文件型指针
代码演示:
<1>、fwrite
int main()
{
struct S stu = { "zhangsan",20,120.0f };
FILE* pf = fopen("test.txt", "w");
if(pf == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
fwrite(&stu, sizeof(struct S), 1, pf);
fclose(pf);
system("pause");
return 0;
}
运行后查看 test.txt 文件的内容:
我们发现里面存放的我们并不认识的东西,这是因为 fwrite 是以二进制的形式存放的,如果我们想要看里面的内容,当然需要使用二进制的形式查看,因此,下面是fread 代码演示。
<2>、fread
int main()
{
struct S stu = { 0 };
FILE* pf = fopen("test.txt", "r");
if(pf == NULL)
{
perror("The following error occurred");
exit(EXIT_FAILURE); //失败返回
}
fread(&stu, sizeof(struct S), 1, pf);
printf("%s %d %f\n", stu.name, stu.age, stu.score);
fclose(pf);
system("pause");
return 0;
}
运行结果:
结果得到的是我们刚刚存放进去的数据。