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
繁體地址,請注明出處:http://hk.pswp.cn/web/83567.shtml
英文地址,請注明出處:http://en.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;負責在復雜的網絡環境中將數據…