C语言 文件读写

本章我们将介绍 C 程序员如何创建、打开、关闭文本文件或二进制文件。

一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。C 语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件。本章将讲解文件管理的重要调用。

打开文件

您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:

FILE *fopen( const char * filename, const char * mode );

在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:

模式 描述
r 打开一个已有的文本文件,允许读取文件。
w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:

"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

关闭文件

为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:

 int fclose( FILE *fp );

如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。

C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。

写入文件

下面是把字符写入到流中的最简单的函数:

int fputc( int c, FILE *fp );

函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。您可以使用下面的函数来把一个以 null 结尾的字符串写入到流中:

int fputs( const char *s, FILE *fp );

函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。您也可以使用 int fprintf(FILE *fp,const char *format, ...) 函数来写把一个字符串写入到文件中。尝试下面的实例:

注意:请确保您有可用的 /tmp 目录,如果不存在该目录,则需要在您的计算机上先创建该目录。

#include <stdio.h>int main()
{FILE *fp;fp = fopen("./tmp/test.txt", "w+");fprintf(fp, "This is testing for fprintf...\n");fputs("This is testing for fputs...\n", fp);fclose(fp);
}

当上面的代码被编译和执行时,它会在 /tmp 目录中创建一个新的文件 test.txt,并使用两个不同的函数写入两行。接下来让我们来读取这个文件。

读取文件

下面是从文件读取单个字符的最简单的函数:

int fgetc( FILE * fp );

fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。下面的函数允许您从流中读取一个字符串:

char *fgets( char *buf, int n, FILE *fp );

函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。

如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE *fp, const char *format, ...) 函数来从文件中读取字符串,但是在遇到第一个空格字符时,它会停止读取。

二进制 I/O 函数

下面两个函数用于二进制输入和输出:

size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);

这两个函数都是用于存储块的读写 - 通常是数组或结构体。

实例:

文件打开输出就用:

 1 #include <stdio.h>2  3 int main()4 {5    FILE *fp = NULL;6  7    fp = fopen("/tmp/test.txt", "w+"); //第一个逗号前是文件位置。逗号之后是打开文件方式8    fprintf(fp, "This is testing for fprintf...\n");  //逗号之前是一个指针,表明往里面输入。逗号之后fprintf是往文件里面输入9    fputs("This is testing for fputs...\n", fp);
10    fclose(fp);  //记得用完关闭文件
11 }

文件打开读取:

 1 #include <stdio.h>2  3 int main()4 {5    FILE *fp = NULL;6    char buff[255];7  8    fp = fopen("/tmp/test.txt", "r");9    fscanf(fp, "%s", buff);  //写入的时候和平常没有区别,还是只有字符串变量前不加‘&’,其他int、double等类型前都要加‘&’符号
10    printf("1: %s\n", buff );
11
12    fgets(buff, 255, (FILE*)fp);  //scanf遇到空格就会断开,gets会读取空格,遇到换行就结束
13    printf("2: %s\n", buff );     //255是限制最大读取内容长度
14
15    fgets(buff, 255, (FILE*)fp);
16    printf("3: %s\n", buff );
17    fclose(fp);
18
19 }

文件读去和写入:

文件判断是否结尾要用feof()函数

 1 #include <stdio.h>2 int main()3 {4    FILE *fp = NULL;5    double buff;6    double s;7    int w;8    scanf("%lf",&s);9    w=s;
10    fp = fopen("coursese.txt", "w");
11    fprintf(fp,"%lf %lf %d",s,s,w);  //这个%d后面不能加'\n',因为在文件中虽然一行什么东西都没有但是这一行确实存在,那么就不会
12    fclose(fp);                      //遇到文件结束标志。不仅换行不能交,空格也不能交
13    //即fprintf(fp,"%lf %lf %d ",s,s,w);、fprintf(fp,"%lf %lf %d ",s,s,w);  这两种形式都错
14    fp = fopen("coursese.txt", "r");
15    while(1){
16         if(feof(fp)) break;
17         fscanf(fp, "%lf%lf%d", &buff,&s,&w);
18         printf("%lf %lf %d\n",buff,s,w);
19    }
20    fclose(fp);
21 }

加上%s也可以:

 1 #include <stdio.h>2 int main()3 {4    FILE *fp = NULL;5    double buff;6    double s;7    int w;8    char ss[55];9    scanf("%lf",&s);
10    scanf("%s",ss);
11    w=s;
12    fp = fopen("coursese.txt", "w");
13    fprintf(fp,"%lf %lf %d %s",s,s,w,ss);  //这个%d后面不能加'\n',因为在文件中虽然一行什么东西都没有但是这一行确实存在,那么就不会
14    fclose(fp);                      //遇到文件结束标志。不仅换行不能交,空格也不能交
15    //即fprintf(fp,"%lf %lf %d ",s,s,w);、fprintf(fp,"%lf %lf %d ",s,s,w);  这两种形式都错
16    fp = fopen("coursese.txt", "r");
17    while(1){
18         if(feof(fp)) break;
19         fscanf(fp, "%lf%lf%d%s", &buff,&s,&w,ss);
20         printf("%lf %lf %d %s\n",buff,s,w,ss);
21    }
22    fclose(fp);
23 }

还有一种判断文件结束方式:fgetc()

但是这个函数相当于getchar(),它会在文件中吸取一个字符,这样的话文件指针就会向后移动一位,从而导致拿出来的时候数据和进去的时候不一样

代码:

 1 #include <stdio.h>2 int main()3 {4    FILE *fp = NULL;5    double buff;6    double s;7    int w;8    char ss[55];9    scanf("%lf",&s);
10    scanf("%s",ss);
11    w=s;
12    fp = fopen("coursese.txt", "w");
13    fprintf(fp,"%lf %lf %d %s",s,s,w,ss);
14    fclose(fp);
15    fp = fopen("coursese.txt", "r");
16
17    char ch;
18    while(1){
19         ch=fgetc(fp);
20         if(ch==EOF) break;
21         fscanf(fp, "%lf%lf%d%s", &buff,&s,&w,ss);
22         printf("%lf %lf %d %s\n",buff,s,w,ss);
23    }
24    fclose(fp);
25 }

考虑到它判断文件的方式,我们可以输入的时候在每一条数据前面多加一个空格,来充当那个fgetc吸收的无用字符

代码:

 1 #include <stdio.h>2 int main()3 {4    FILE *fp = NULL;5    double buff;6    double s;7    int w;8    char ss[55];9    scanf("%lf",&s);
10    scanf("%s",ss);
11    w=s;
12    fp = fopen("coursese.txt", "w");
13    fprintf(fp," %lf %lf %d %s",s,s,w,ss);  //前面多加了一个空格。也可以加其他
14    fclose(fp);
15    fp = fopen("coursese.txt", "r");
16
17    char ch;
18    while(1){
19         ch=fgetc(fp);
20         if(ch==EOF) break;
21         fscanf(fp, "%lf%lf%d%s", &buff,&s,&w,ss);
22         printf("%lf %lf %d %s\n",buff,s,w,ss);
23    }
24    fclose(fp);
25 }

C语言 预处理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

预处理器实例

分析下面的实例来理解不同的指令。

#define MAX_ARRAY_LENGTH 20

这个指令告诉 CPP 把所有的 MAX_ARRAY_LENGTH 替换为 20。使用 #define 定义常量来增强可读性。

#include <stdio.h>
#include "myheader.h"

这些指令告诉 CPP 从系统库中获取 stdio.h,并添加文本到当前的源文件中。下一行告诉 CPP 从本地目录中获取 myheader.h,并添加内容到当前的源文件中。

#undef  FILE_SIZE
#define FILE_SIZE 42

这个指令告诉 CPP 取消已定义的 FILE_SIZE,并定义它为 42。

#ifndef MESSAGE#define MESSAGE "You wish!"
#endif

这个指令告诉 CPP 只有当 MESSAGE 未定义时,才定义 MESSAGE。

#ifdef DEBUG/* Your debugging statements here */
#endif

这个指令告诉 CPP 如果定义了 DEBUG,则执行处理语句。在编译时,如果您向 gcc 编译器传递了 -DDEBUG 开关量,这个指令就非常有用。它定义了 DEBUG,您可以在编译期间随时开启或关闭调试。

预定义宏

ANSI C 定义了许多宏。在编程中您可以使用这些宏,但是不能直接修改这些预定义的宏。

描述
__DATE__ 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
__TIME__ 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
__FILE__ 这会包含当前文件名,一个字符串常量。
__LINE__ 这会包含当前行号,一个十进制常量。
__STDC__ 当编译器以 ANSI 标准编译时,则定义为 1。

让我们来尝试下面的实例:

#include <stdio.h>int main()
{printf("File :%s\n", __FILE__ );printf("Date :%s\n", __DATE__ );printf("Time :%s\n", __TIME__ );printf("Line :%d\n", __LINE__ );printf("ANSI :%d\n", __STDC__ );}

尝试一下

当上面的代码(在文件 test.c 中)被编译和执行时,它会产生下列结果:

File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1

预处理器运算符

C 预处理器提供了下列的运算符来帮助您创建宏:

宏延续运算符(\)

一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。例如:

#define  message_for(a, b)  \printf(#a " and " #b ": We love you!\n")

字符串常量化运算符(#)

在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如:

#include <stdio.h>#define  message_for(a, b)  \printf(#a " and " #b ": We love you!\n")int main(void)
{message_for(Carole, Debra);return 0;
}

尝试一下

当上面的代码被编译和执行时,它会产生下列结果:

Carole and Debra: We love you!

标记粘贴运算符(##)

宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:

#include <stdio.h>#define tokenpaster(n) printf ("token" #n " = %d", token##n)int main(void)
{int token34 = 40;tokenpaster(34);return 0;
}

尝试一下

当上面的代码被编译和执行时,它会产生下列结果:

token34 = 40

这是怎么发生的,因为这个实例会从编译器产生下列的实际输出:

printf ("token34 = %d", token34);

这个实例演示了 token##n 会连接到 token34 中,在这里,我们使用了字符串常量化运算符(#)标记粘贴运算符(##)

defined() 运算符

预处理器 defined 运算符是用在常量表达式中的,用来确定一个标识符是否已经使用 #define 定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。下面的实例演示了 defined() 运算符的用法:

#include <stdio.h>#if !defined (MESSAGE)#define MESSAGE "You wish!"
#endifint main(void)
{printf("Here is the message: %s\n", MESSAGE);  return 0;
}

尝试一下

当上面的代码被编译和执行时,它会产生下列结果:

Here is the message: You wish!

参数化的宏

CPP 一个强大的功能是可以使用参数化的宏来模拟函数。例如,下面的代码是计算一个数的平方:

int square(int x) {return x * x;
}

我们可以使用宏重写上面的代码,如下:

#define square(x) ((x) * (x))

在使用带有参数的宏之前,必须使用 #define 指令定义。参数列表是括在圆括号内,且必须紧跟在宏名称的后边。宏名称和左圆括号之间不允许有空格。例如:

#include <stdio.h>#define MAX(x,y) ((x) > (y) ? (x) : (y))int main(void)
{printf("Max between 20 and 10 is %d\n", MAX(10, 20));  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Max between 20 and 10 is 20

C语言入门教程||C语言 文件读写||C语言 预处理器相关推荐

  1. 《零基础看得懂的C语言入门教程 》——(四)C语言的基本数据类型及变量

    一.学习目标 了解C语言的基本数据类型 了解变量的基本概念 了解变量的使用方法 了解了变量的命名方法 了解格式占位符 了解变量的输出 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. ...

  2. 《零基础看得懂的C语言入门教程 》——(五)C语言的变量、常量及运算

    一.学习目标 了解C语言变量的其它创建方式 了解C语言常量 了解C语言的运算符 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一篇:(一)脱离学习误区 第二篇:(二)C语言没那 ...

  3. 《零基础看得懂的C语言入门教程 》——(十一)C语言自定义函数真的很简单

    一.学习目标 了解C语言的自定义函数的使用方法 了解C语言自定义函数的传参 了解C语言自定义函数的返回值 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一篇:(一)脱离学习误区 ...

  4. 《零基础看得懂的C语言入门教程 》——(二)C语言没那么难简单开发带你了解流程

    一.学习目标 了解DevC集成开发环境 了解集成开发环境 了解HelloWorld程序 了解HelloWorld程序的编写方法 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一 ...

  5. 《零基础看得懂的C语言入门教程 》——(七)C语言的循环分分钟上手

    一.学习目标 了解循环的使用方法 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一篇:(一)脱离学习误区 第二篇:(二)C语言没那么难简单开发带你了解流程 第三篇:(三)轻轻松 ...

  6. 《零基础看得懂的C语言入门教程 》——(十)C语言的指针原来是这样

    一.学习目标 了解指针的概念 了解指针的使用方法 了解双重指针 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一篇:(一)脱离学习误区 第二篇:(二)C语言没那么难简单开发带你 ...

  7. 《零基础看得懂的C语言入门教程 》——(九)C语言二维数组与循环嵌套

    一.学习目标 了解二维数组的使用方法 了解循环嵌套的使用方法 目录 C语言真的很难吗?那是你没看这张图,化整为零轻松学习C语言. 第一篇:(一)脱离学习误区 第二篇:(二)C语言没那么难简单开发带你了 ...

  8. iOS开发之c语言入门教程

    苹果作为移动互联的高端品牌,iOS操作系统也被用户公认为是最好用的移动互联网操作系统.据了解,曾一度拒绝为iOS平台对出浏览器的火狐在今年5月份的时候,他们却食言了.今天,Mozilla宣布iOS版F ...

  9. R语言七天入门教程六:文件相关操作

    R语言七天入门教程六:文件相关操作 一.文件的读写 R 语言作为统计学编程语言,常常需要处理大量数据,而这些数据通常会从文件中进行读取,因此文件读写在R语言中是非常重要的操作.在R语言中,用到最多的文 ...

  10. c语言5基础教程,[简001]《极简C语言入门教程》共5章

    Saturday,May 18,2019 ---Andy ###目录: 前言 第一章 数据类型 1.1 数据类型 1.2 宏定义.常量.变量(一般和指针型) 第二章 格式化输入输出 2.1 输入 2. ...

最新文章

  1. sql%notfound与exception
  2. Nginx一点事儿(一)
  3. GARFIELD@02-25-2005
  4. java robot 对象_用Java Robot对象实现服务器屏幕远程监视
  5. POJ 1833 排列【STL/next_permutation】
  6. java 8是指什么_java中8个基本数据类型到底是指什么?是什么意思,有什么作用?我需要权威的回答,...
  7. python读取yaml文件
  8. 聚类效果评价——Silhouette Coefficient(轮廓系数)——内部评估标准(1)
  9. Java中@WebServlet的使用方法
  10. 如何成为数据科学家_成为数据科学家需要了解的10件事
  11. python——redis
  12. Python random模块sample、randint、shuffle、choice随机函数
  13. bat批量修改及替换文件内容
  14. 推荐5个优秀的Java开源项目,初学者友好
  15. 计算机辅助设计cad实训总结,CAD画图的心得体会
  16. 视频教程-Windows程序设计应用开发-C/C++
  17. html网页中加入音乐播放器,html 网页添加音乐播放器
  18. Java将汉字转为拼音
  19. 英语钻石法则(七)——【听-问答-复述故事】
  20. VBA编程图表(二十一)

热门文章

  1. 出生证明电子版到哪里查看_注意注意,你家孩子的出生医学证明档案可电子查阅了...
  2. Buffon投针实验:究竟为什么是pi?
  3. 动态规划的一般解题思路-详解
  4. 邮件 发送excel表格做正文
  5. 机器学习项目实战----新闻分类任务(二)
  6. Git操作 【详细】【详细】
  7. 这里记录几个国外的网站
  8. ios: Undefined symbols
  9. SolidWorks装配模块四连杆运动仿真…
  10. 外仁内圣,以借得天下,以情御英雄