C语言文件操作与预处理详解

目录

    • 文件操作
      • 文件基本概念
      • 文件指针
      • 文件打开模式
      • 文件读取操作
        • 字符读取
        • 字符串读取
        • 格式化读取
        • 二进制读取
      • 文件写入操作
        • 字符写入
        • 字符串写入
        • 格式化写入
        • 二进制写入
      • 文件定位操作
      • 文件错误处理
    • 预处理
      • 预处理基本概念
      • 常见预处理指令
      • 文件包含指令
      • 宏定义
        • 简单宏
        • 带参数的宏
        • 字符串化操作符(#)
        • 标记粘贴操作符(##)
      • 条件编译
        • #ifdef 和 #ifndef
        • #if 和 #elif
        • 条件编译示例:平台特定代码
      • 其他预处理指令
        • #error 指令
        • #line 指令
        • #pragma 指令
      • 预处理的优缺点
        • 优点
        • 缺点
      • 预处理与编译的区别
    • 实际应用示例
      • 文件操作示例:学生成绩管理系统
      • 预处理示例:调试宏
    • 总结

在C语言编程中,文件操作和预处理是两个重要的组成部分。文件操作允许程序与外部存储设备交互,而预处理则在编译前对源代码进行文本处理。这两个功能为C程序提供了强大的扩展性和灵活性。

文件操作

文件基本概念

在C语言中,文件是存储在外部介质(如硬盘、U盘)上的数据集合。C语言将文件视为字节序列,并提供了两种文件处理模式:

  1. 文本模式:以字符为单位处理文件,自动处理换行符(Windows:\r\n ↔ Unix:\n
  2. 二进制模式:以字节为单位处理文件,不进行任何转换

文件指针

文件操作通过文件指针(FILE*)实现,它指向一个包含文件信息的结构体:

#include <stdio.h>FILE *fp;  // 声明文件指针// 打开文件
fp = fopen("example.txt", "r");  // 以只读模式打开文本文件if (fp == NULL) {printf("无法打开文件\n");return 1;
}// 文件操作...// 关闭文件
fclose(fp);

文件打开模式

模式描述
“r”只读模式,文件必须存在
“w”写入模式,创建新文件或覆盖
“a”追加模式,在文件末尾添加内容
“r+”读写模式,文件必须存在
“w+”读写模式,创建新文件或覆盖
“a+”读写模式,在文件末尾添加内容

文件读取操作

字符读取
#include <stdio.h>int main() {FILE *fp = fopen("example.txt", "r");if (fp == NULL) {printf("无法打开文件\n");return 1;}int ch;// 逐个字符读取,EOF表示文件结束while ((ch = fgetc(fp)) != EOF) {putchar(ch);  // 输出到屏幕}fclose(fp);return 0;
}
字符串读取
#include <stdio.h>int main() {FILE *fp = fopen("example.txt", "r");if (fp == NULL) {printf("无法打开文件\n");return 1;}char buffer[100];// 读取一行文本(最多99个字符)while (fgets(buffer, sizeof(buffer), fp) != NULL) {printf("%s", buffer);}fclose(fp);return 0;
}
格式化读取
#include <stdio.h>int main() {FILE *fp = fopen("data.txt", "r");if (fp == NULL) {printf("无法打开文件\n");return 1;}int num;float f;char str[50];// 格式化读取while (fscanf(fp, "%d %f %s", &num, &f, str) == 3) {printf("读取: %d, %.2f, %s\n", num, f, str);}fclose(fp);return 0;
}
二进制读取
#include <stdio.h>typedef struct {int id;char name[50];float score;
} Student;int main() {FILE *fp = fopen("students.dat", "rb");  // 二进制读取if (fp == NULL) {printf("无法打开文件\n");return 1;}Student s;// 读取结构体数据while (fread(&s, sizeof(Student), 1, fp) == 1) {printf("ID: %d, 姓名: %s, 分数: %.2f\n", s.id, s.name, s.score);}fclose(fp);return 0;
}

文件写入操作

字符写入
#include <stdio.h>int main() {FILE *fp = fopen("output.txt", "w");if (fp == NULL) {printf("无法创建文件\n");return 1;}char text[] = "Hello, World!";for (int i = 0; text[i] != '\0'; i++) {fputc(text[i], fp);  // 写入单个字符}fclose(fp);return 0;
}
字符串写入
#include <stdio.h>int main() {FILE *fp = fopen("output.txt", "w");if (fp == NULL) {printf("无法创建文件\n");return 1;}char *text = "这是一行文本\n";fputs(text, fp);  // 写入字符串(不自动添加换行符)fclose(fp);return 0;
}
格式化写入
#include <stdio.h>int main() {FILE *fp = fopen("data.txt", "w");if (fp == NULL) {printf("无法创建文件\n");return 1;}int num = 42;float f = 3.14;char *str = "Hello";// 格式化写入fprintf(fp, "%d %.2f %s\n", num, f, str);fclose(fp);return 0;
}
二进制写入
#include <stdio.h>typedef struct {int id;char name[50];float score;
} Student;int main() {FILE *fp = fopen("students.dat", "wb");  // 二进制写入if (fp == NULL) {printf("无法创建文件\n");return 1;}Student s = {1, "张三", 85.5};// 写入结构体数据fwrite(&s, sizeof(Student), 1, fp);fclose(fp);return 0;
}

文件定位操作

#include <stdio.h>int main() {FILE *fp = fopen("example.txt", "r");if (fp == NULL) {printf("无法打开文件\n");return 1;}// 获取文件位置long pos = ftell(fp);  // 初始位置为0// 移动文件指针fseek(fp, 10, SEEK_SET);  // 从文件开头移动10个字节fseek(fp, 5, SEEK_CUR);   // 从当前位置移动5个字节fseek(fp, -20, SEEK_END); // 从文件末尾向前移动20个字节// 重置文件指针到开头rewind(fp);fclose(fp);return 0;
}

文件错误处理

#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {FILE *fp = fopen("nonexistent.txt", "r");if (fp == NULL) {// 打印错误信息printf("错误: %s\n", strerror(errno));return 1;}// 检查文件操作错误if (ferror(fp)) {printf("文件操作错误\n");clearerr(fp);  // 清除错误标志}fclose(fp);return 0;
}

预处理

预处理基本概念

预处理是C编译过程的第一步,由预处理器(Preprocessor)执行。预处理指令以#开头,它们在编译前被处理,用于修改源代码文本。

常见预处理指令

  1. 文件包含#include
  2. 宏定义#define#undef
  3. 条件编译#if#ifdef#ifndef#elif#else#endif
  4. 错误处理#error
  5. 行号和文件名#line
  6. 编译控制#pragma

文件包含指令

// 标准库头文件
#include <stdio.h>    // 从标准库目录查找
#include <string.h>// 自定义头文件
#include "myheader.h" // 从当前目录或指定目录查找

宏定义

简单宏
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))int main() {float radius = 5.0;float area = PI * radius * radius;int x = 10, y = 20;int max_val = MAX(x, y);return 0;
}
带参数的宏
#define SQUARE(x) ((x) * (x))
#define PRINT_INT(x) printf("Value: %d\n", x)int main() {int a = 5;int result = SQUARE(a + 1);  // 展开为 ((a + 1) * (a + 1))PRINT_INT(result);  // 展开为 printf("Value: %d\n", result)return 0;
}
字符串化操作符(#)
#define STR(x) #xint main() {printf(STR(Hello World!));  // 展开为 printf("Hello World!");printf(STR(1 + 2));         // 展开为 printf("1 + 2");return 0;
}
标记粘贴操作符(##)
#define CONCAT(a, b) a##bint main() {int xy = 100;printf("%d\n", CONCAT(x, y));  // 展开为 printf("%d\n", xy);return 0;
}

条件编译

#ifdef 和 #ifndef
#ifdef DEBUGprintf("调试信息: 变量x = %d\n", x);
#endif#ifndef MAX_SIZE#define MAX_SIZE 100
#endif
#if 和 #elif
#define VERSION 2#if VERSION == 1printf("使用版本1\n");
#elif VERSION == 2printf("使用版本2\n");
#elseprintf("未知版本\n");
#endif
条件编译示例:平台特定代码
#ifdef _WIN32// Windows平台代码#include <windows.h>#define LINE_END "\r\n"
#elif __linux__// Linux平台代码#include <unistd.h>#define LINE_END "\n"
#else#error "不支持的平台"
#endif

其他预处理指令

#error 指令
#if !defined(__STDC__)#error "需要标准C编译器"
#endif
#line 指令
#line 100 "custom_file.c"
// 从这里开始,行号从100开始,文件名显示为custom_file.c
#pragma 指令
#pragma once  // 保证头文件只被包含一次#pragma GCC diagnostic ignored "-Wunused-variable"  // 忽略未使用变量警告

预处理的优缺点

优点
  1. 代码复用:通过宏和头文件实现代码重用
  2. 条件编译:支持跨平台开发和调试版本
  3. 代码生成:在编译前自动生成代码
  4. 性能优化:宏替换可以减少函数调用开销
缺点
  1. 可读性降低:过度使用宏会使代码难以理解
  2. 调试困难:错误可能出现在预处理后的代码中
  3. 潜在副作用:宏参数可能被多次求值
  4. 命名冲突:宏定义可能与其他标识符冲突

预处理与编译的区别

特性预处理阶段编译阶段
执行时间编译前预处理后
处理内容文本替换、文件包含、条件编译词法分析、语法分析、代码生成
输出结果修改后的源代码目标代码(汇编或机器码)
工具预处理器(cpp)编译器(cc、gcc)
指令形式以#开头的预处理指令C语言语句

实际应用示例

文件操作示例:学生成绩管理系统

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX_NAME_LEN 50
#define MAX_STUDENTS 100typedef struct {int id;char name[MAX_NAME_LEN];float score;
} Student;// 保存学生信息到文件
void saveStudents(Student students[], int count) {FILE *fp = fopen("students.dat", "wb");if (fp == NULL) {printf("无法创建文件\n");return;}fwrite(&count, sizeof(int), 1, fp);  // 写入学生数量fwrite(students, sizeof(Student), count, fp);  // 写入学生数据fclose(fp);printf("已保存 %d 名学生信息\n", count);
}// 从文件加载学生信息
int loadStudents(Student students[]) {FILE *fp = fopen("students.dat", "rb");if (fp == NULL) {printf("无法打开文件或文件不存在\n");return 0;}int count;fread(&count, sizeof(int), 1, fp);  // 读取学生数量fread(students, sizeof(Student), count, fp);  // 读取学生数据fclose(fp);printf("已加载 %d 名学生信息\n", count);return count;
}int main() {Student students[MAX_STUDENTS];int count = 0;// 添加学生信息students[count].id = 1;strcpy(students[count].name, "张三");students[count].score = 85.5;count++;students[count].id = 2;strcpy(students[count].name, "李四");students[count].score = 92.0;count++;// 保存到文件saveStudents(students, count);// 清空数组memset(students, 0, sizeof(students));count = 0;// 从文件加载count = loadStudents(students);// 显示学生信息for (int i = 0; i < count; i++) {printf("ID: %d, 姓名: %s, 分数: %.1f\n", students[i].id, students[i].name, students[i].score);}return 0;
}

预处理示例:调试宏

#ifdef DEBUG#define DEBUG_PRINT(fmt, ...) printf("DEBUG: " fmt, __VA_ARGS__)#define DEBUG_LINE() printf("DEBUG: Line %d in %s\n", __LINE__, __FILE__)
#else#define DEBUG_PRINT(fmt, ...) do {} while(0)#define DEBUG_LINE() do {} while(0)
#endif// 平台检测
#ifdef _WIN32#define PLATFORM "Windows"#include <windows.h>
#elif __linux__#define PLATFORM "Linux"#include <unistd.h>
#elif __APPLE__#define PLATFORM "macOS"#include <unistd.h>
#else#define PLATFORM "Unknown"
#endifint main() {DEBUG_LINE();DEBUG_PRINT("程序开始运行,平台: %s\n", PLATFORM);int x = 42;DEBUG_PRINT("变量x的值: %d\n", x);// 正常代码...return 0;
}

总结

文件操作和预处理是C语言中两个重要的功能,它们分别在程序运行时和编译前发挥作用:

  • 文件操作允许程序与外部存储设备交互,支持文本和二进制两种模式
  • 预处理在编译前对源代码进行文本处理,提供宏定义、文件包含和条件编译等功能
  • 文件操作通过文件指针和标准库函数实现,需要注意文件打开模式和错误处理
  • 预处理指令以#开头,它们不是C语言语句,而是由预处理器单独处理

掌握文件操作和预处理技术,能够使C程序更加灵活、可移植,并支持复杂的数据处理和代码组织。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

水库大坝安全监测之渗流监测

水库大坝的渗流状况直接关系到其结构稳定性与安全运行。渗流可能引发坝体内部土体的渗透变形&#xff0c;如管涌、流土等现象&#xff0c;削弱坝体强度&#xff0c;严重时甚至导致大坝垮塌&#xff0c;威胁下游人民生命财产安全。通过渗流监测&#xff0c;能够实时掌握坝体及坝…

windows使用命令行查看进程信息

在 Windows 操作系统中&#xff0c;您可以使用多种命令行工具来查看进程信息。以下是几种常用方法&#xff1a; 1. 使用 tasklist 命令&#xff08;最常用&#xff09; 查看所有进程的基本信息&#xff1a; tasklist输出示例&#xff1a; 映像名称 PID…

【C#】多级缓存与多核CPU

多级缓存&#xff08;如CPU的L1/L2/L3缓存&#xff09;与多核处理器之间存在紧密的协同与竞争关系&#xff0c;直接影响系统性能。以下是关键影响及优化策略&#xff1a; 一、缓存层级与多核的协作机制 缓存结构 L1缓存 私有缓存&#xff1a;每个CPU核心独享&#xff0c;容量小…

PostgreSQL的扩展adminpack

PostgreSQL的扩展adminpack adminpack 是 PostgreSQL 提供的一个管理扩展&#xff0c;它包含多个实用函数&#xff0c;帮助数据库管理员执行文件系统操作和维护任务。这个扩展通常由数据库超级用户使用&#xff0c;提供了一些服务器端的文件访问功能。 一、adminpack 扩展概述…

Unity | AmplifyShaderEditor插件基础(第九集:旗子进阶版)

目录 一、&#x1f44b;&#x1f3fb;前言 二、准备工作 1.下载安装插件ProBuilder 2.下载安装插件Polybrush 3.固定原理 4.旗子 三、顶点上色 1.创建一个可以顶点上色的材质 2.开始上色 a.上色功能说明 b.全部上色 c.调整刷子 四、shader的设置 1.幅度添加 2.顶…

Java 实现 Excel 转化为 PDF

引言 在实际开发中&#xff0c;将 Excel 文件转化为 PDF 格式是一项常见需求。例如在需要共享数据报表时&#xff0c;PDF 格式具有更好的兼容性和安全性。GrapeCity Documents for Excel&#xff08;GcExcel&#xff09;为 Java 开发者提供了强大的工具&#xff0c;可轻松实现…

Spring Boot3批式访问Dify聊天助手接口

Spring Boot3批式访问Dify聊天助手接口 前言 之前已经配置好Dify1.4.1及LM Studio集成&#xff1a; https://lizhiyong.blog.csdn.net/article/details/148607462 现在就可以借助Spring Boot3去访问Dify的后端接口&#xff0c;让前端展示大模型的返回内容。这是我等大数据资…

事务传播行为详解

一、事务传播行为的基本概念 事务传播行为是Spring 框架中事务管理的核心概念&#xff0c;用于定义当一个事务方法被另一个事务方法调用时&#xff0c;事务应如何传播。通俗地说&#xff0c;它解决了 “多个事务方法嵌套调用时&#xff0c;新方法是加入现有事务还是创建新事务…

Java八股文——Spring「SpringMVC 篇」

MVC分层介绍一下 面试官您好&#xff0c;MVC是一种非常经典、影响深远的软件设计模式&#xff0c;它的全称是Model-View-Controller。在我看来&#xff0c;它的核心目标就是解决早期Web开发中&#xff0c;业务逻辑、数据和界面显示高度耦合的问题&#xff0c;从而实现“各司其…

FreeSWITCH mod_curl 和 mod_xml_rpc 测试

编辑 /usr/local/freeswitch/conf/autoload_configs/xml_rpc.conf.xml <configuration name"xml_rpc.conf" description"XML RPC"> <settings> <param name"http-port" value"8889"/> <param name&quo…

实时监控、秒级决策:镜舟科技如何重塑融资融券业务数据处理模式

融资融券业务作为证券市场的重要组成部分&#xff0c;已成为金融机构核心业务增长点和利润来源。截至 2023 年底&#xff0c;我国融资融券余额已突破 1.8 万亿元&#xff0c;业务量呈现爆发式增长。然而&#xff0c;在业务高速发展的同时&#xff0c;金融机构面临着数据处理效率…

Linux与量子计算:面向未来的架构演进

Linux与量子计算&#xff1a;面向未来的架构演进 当经典计算遇上量子革命 引言&#xff1a;量子计算时代的黎明 量子计算正从理论走向工程实践&#xff0c;Linux作为现代计算的基石&#xff0c;正在量子革命中扮演关键角色。据IBM预测&#xff0c;到2027年&#xff0c;量子优势…

Java中wait()为何必须同步调用?

在 Java 中&#xff0c;wait() 方法必须在 synchronized 方法或代码块中调用&#xff0c;主要原因如下&#xff1a; 1. 监视器锁&#xff08;Monitor&#xff09;机制 依赖对象锁&#xff1a;wait() 方法需要操作对象的监视器锁&#xff08;Monitor&#xff09;&#xff0c;调…

前端面试专栏-基础篇:4. 页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…

涨薪技术|Docker端口映射与容器互联技术

前面的推文我们学了Docker操作的常用命令,今天开始给大家分享Docker端口映射与容器互联,欢迎关注。Docker不管是程序员,架构师或者测试工程师都必须要掌握的一门主流技术。 Docker除了通过网络访问外,还提供了两个很方便的功能来满足服务访问的基本需求,一个是允许映射容…

Roboguide工作站机器人重新安装软件包

1、点击菜单栏“机器人-属性”&#xff1b; 2、点击“重新生成”&#xff1b; 3、点击“确定”&#xff1b; 4、点击“6&#xff1a;机器人选项” 5、在搜索框搜索软件包&#xff0c;或在软件包列表选择&#xff0c;勾选软件包后点击“下一步”&#xff1b; 6、点击“完成”&am…

预训练CNN网络的迁移学习(MATLAB例)

从基于大型数据集训练的神经网络中提取层&#xff0c;并基于新数据集进行微调。本例使用ImageNet中的子集进行微调。 This example retrains a SqueezeNet neural network using transfer learning. This network has been trained on over a million images, and can classif…

kali系统 windows Linux靶机入侵演练

Kali系统与Windows/Linux靶机入侵演练简介 演练概述 Kali Linux是一款专为渗透测试和网络安全评估设计的操作系统,常被安全专业人员用于合法的安全测试。入侵演练是网络安全训练的重要组成部分,旨在帮助安全人员了解攻击手法并提升防御能力。 基本组件 1. **攻击机**:通常…

手搓transformer

思路是这样子的&#xff1a;从手搓代码的角度去学习transformer&#xff0c;代码会一个一个模块地从头到尾添加&#xff0c;以便学习者跟着敲&#xff0c;到最后再手搓一个基于tansformer的机器翻译实战项目。 transformer整体架构 一、输入部分 词向量 import torch import t…

网络层协议:IP

目录 1、概念 2、关键组成部分 2.1 IP地址 2.1.1 概念 2.1.2 主要版本 2.1.3 IP地址分类 2.2 IP数据报&#xff08;IP协议传输的基本数据单元&#xff09; 3、工作原理 3.1 路由 3.2 分片与重组 4、相关协议 1、概念 目的&#xff1a;负责在复杂的网络环境中将数据…