文件操作和编译环境程序预处理
1.文件操作
1.打开和关闭文件
fopen
和fclose
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
打开的方式
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 建立一个新的文件 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
如果指定文件不存在,读操作会报错,其余的不会,会创建新的文件.如果之前存在,则写操作会生成新的文件覆盖之前的文件.
代码样例
#include<stdio.h>
//读文件操作
int main()
{
int a = 10;
double b = 100;
int c = 0;
double d = 0;
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
/*fprintf(pf,"%d %lf",a,b);*/
fscanf(pf, "%d %lf",&c,&d);
fclose(pf);
pf = NULL;
return 0;
}
2.文件的顺序读写
声明一点:读文件是我读文件,对我而言就是输入,和我写文件,对我而言就是输出。
接下来对文件操作的函数进行解释说明
1.fgetc fputc fgets fputs
代码使用
int fgetc ( FILE * stream );//一次读一个字符
int fputc ( int character, FILE * stream );//一次放一个字符
char * fgets ( char * str, int num, FILE * stream );//一次取若干个字节的字符,n表示数量
int fputs ( const char * str, FILE * stream );//将str放入文件中
//FILE * stream表示文件指针
例子
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
//printf("%s", strerror(errno));
return 1;
}
//写文件
/*for(int i='a';i<='z';i++)
fputc(i, pf);*/
//int ch= fgetc(pf);
//printf("%c\n",ch);
// ch = fgetc(pf);
//printf("%c\n", ch);
// ch = fgetc(pf);
//printf("%c\n", ch);
// ch = fgetc(pf);
//printf("%c\n", ch);
// ch = fgetc(pf);
//printf("%c\n", ch);
// ch = fgetc(pf);
//printf("%c\n", ch);
//int ch;
//while ((ch=fgetc(pf))!=EOF)
//{
// printf("%c\n", ch);
//}
/*fputs("hello bit", pf);*/
char arr[20];
fgets(arr, 5, pf);
printf("%s", arr);
fclose(pf);
pf = NULL;
return 0;
}
2.fscanf fprintf fwrite freadc
代码使用
int fscanf ( FILE * stream, const char * format, ... );//格式化输入,与scanf相比就多了第一个文件指针
int fprintf ( FILE * stream, const char * format, ... );//格式化输出,与printf相比就多了第一个文件指针
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
//ptr目标指针 size类型解引用大小 count数量 stream文件指针
举例
//fscanf fprintf
#include<stdio.h>
int main()
{
int a = 10;
double b = 100;
int c = 0;
double d = 0;
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
/*fprintf(pf,"%d %lf",a,b);*/
fscanf(pf, "%d %lf",&c,&d);
fclose(pf);
pf = NULL;
return 0;
}
//fwrite fread
#include<stdio.h>
typedef struct add {
char name[20];
int age;
int score;
}stu;
int main()
{
/*stu s1 = { "zhangsan",20,100 };*/
stu s2 = { 0 };
FILE* pf = fopen("text.txt", "ab");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&s2, sizeof(stu), 1, pf);
/*fread(&s2, sizeof(stu), 1, pf);*/
fclose(pf);
pf = NULL;
return 0;
}
3.补充知识 sscanf sprintf
代码使用
int sprintf ( char * str, const char * format, ... );//合并为字符串
int sscanf ( const char * s, const char * format, ...);//分解字符串
/* sscanf example */
#include <stdio.h>
int main ()
{
char sentence []="Rudolph 12 years old";
char str [20];
int i;
sscanf (sentence,"%s %d",str,&i);
printf ("%s -> %d\n",str,i);
return 0;
}
/* sprintf example */
#include <stdio.h>
int main ()
{
char buffer [50];
int n, a=5, b=3;
n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
printf ("[%s] is a string %d chars long\n",buffer,n);
return 0;
}
2.编译环境程序预处理
1.编译环境和阶段
2.预处理详解
1.预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
printf("file:%s line:%d\n", __FILE__, __LINE__);//打印所在源文件,第几行
2.#define定义
#define MAX 1000
定义M为100, 定义时最好不加分号
3.#define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义 宏(define macro)。
#define SQUARE( x ) x * x
在定义宏时加上(),防止其他错误.
例如
int a = 5; printf("%d\n" ,SQUARE( a + 1) );
打印出来的是11,而不是36 因为是替换 ,则原式实际为 5+1*5+1,所以定义宏时加上括号,防止因运算符优先级导致的误差
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。
4.#和##
使用 # ,把一个宏参数变成对应的字符串。
首先,在没有#下
#include<stdio.h>
#define PRINT(N) printf("the value "N" is %d",N);
int main()
{
int a = 10;
PRINT(a);
return 0;
}//报错
//#的作用
#include<stdio.h>
#define PRINT(N) printf("the value "#N" is %d",N);
int main()
{
int a = 10;
PRINT(a);
return 0;
}
##可以把位于它两边的符号合成一个符号
#include<stdio.h>
#define NUM(name,NO) name##NO
int main()
{
int name6 = 1000;
printf("%d", NUM(name, 6));
return 0;
}
5.带有副作用的宏参数
举例
define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//按照宏的定义,则会操作不明确
所以尽量不要使用带副作用的参数
宏和函数对比
#undef
取消#define
定义
6.条件编译
#ifdef #endif
如果什么定义则执行
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
//判断是否定义
#endif
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol//两个等价
//嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
7.文件包含
//方案一
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
//方案二
#pragma once
//避免头文件的重复调用
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。 如果找不到就提示编译错误。
#include <filename>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
对于库文件也可以使用 “” 的形式包含,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
OPTION2
msdos_version_option2();
#endif
#endif
#### 7.文件包含
```c
//方案一
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
//方案二
#pragma once
//避免头文件的重复调用
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。 如果找不到就提示编译错误。
#include <filename>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
对于库文件也可以使用 “” 的形式包含,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。