File I/O และ Preprocessors ใน C
เผยแพร่เมื่อ: 12 สิงหาคม 2025 | หมวดหมู่: C | โดย: GlowCode Team

ข้อกำหนดเบื้องต้น
เมื่อจบบทเรียนนี้ คุณจะสามารถ:
- อ่านและเขียนไฟล์ใน C อย่างมีประสิทธิภาพ
- เข้าใจและใช้งาน preprocessor directives
- สร้าง header files และใช้งาน macros
- จัดการ errors และ exceptions ในการทำงานกับไฟล์
- ใช้ conditional compilation
- วิเคราะห์ file operations ด้วย GlowCode
ความรู้พื้นฐานที่ต้องมี:
พื้นฐาน C, pointers, arrays, strings
File I/O พื้นฐาน
การเปิดและปิดไฟล์
ใน C ใช้ FILE* pointer เพื่อจัดการไฟล์
int fclose(FILE* stream);
โหมดการเปิดไฟล์:
“r” – อ่าน (ไฟล์ต้องมีอยู่แล้ว)
“w” – เขียน (สร้างใหม่หรือเขียนทับ)
“a” – เพิ่มข้อมูลต่อท้าย
“r+” – อ่าน/เขียน
“w+” – อ่าน/เขียน (สร้างใหม่)
“a+” – อ่าน/เพิ่มข้อมูล
ตัวอย่างที่ 1: การเขียนไฟล์
#include <stdlib.h>
int main(void) {
FILE *file = fopen("output.txt", "w");
// ตรวจสอบว่าเปิดไฟล์สำเร็จ
if (file == NULL) {
printf("ไม่สามารถเปิดไฟล์ได้\n");
return 1;
}
// เขียนข้อมูล
fprintf(file, "Hello, File I/O!\n");
fprintf(file, "บรรทัดที่ 2\n");
fprintf(file, "ตัวเลข: %d\n", 42);
// ปิดไฟล์
fclose(file);
printf("เขียนไฟล์สำเร็จ\n");
return 0;
}
⚠️ คำเตือน: ต้องตรวจสอบว่า fopen() return NULL หรือไม่เสมอ และต้องปิดไฟล์ด้วย fclose() เสมอ
💡 เรื่องประสิทธิภาพ: การไม่ปิดไฟล์ทำให้เกิด resource leak และข้อมูลอาจไม่ถูก flush ไปยัง disk
การอ่านไฟล์
ตัวอย่างที่ 2: อ่านไฟล์ทีละบรรทัด
#include <stdlib.h>
int main(void) {
FILE *file = fopen("output.txt", "r");
if (file == NULL) {
printf("ไม่สามารถเปิดไฟล์ได้\n");
return 1;
}
char buffer[256];
printf("เนื้อหาในไฟล์:\n");
printf("-------------------\n");
// อ่านทีละบรรทัด
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
fclose(file);
return 0;
}
📌 Functions สำหรับอ่านไฟล์:
- fgets() – อ่านทีละบรรทัด (แนะนำ – ปลอดภัย)
- fscanf() – อ่านตาม format
- fgetc() – อ่านทีละตัวอักษร
- fread() – อ่านข้อมูล binary
การอ่านและเขียนข้อมูล Binary
ตัวอย่างที่ 3: Binary File Operations
#include <stdlib.h>
typedef struct {
int id;
char name[50];
float score;
} Student;
int main(void) {
Student students[3] = {
{1, "สมชาย", 85.5f},
{2, "สมหญิง", 92.0f},
{3, "สมศรี", 78.5f}
};
// เขียน binary file
FILE *file = fopen("students.dat", "wb");
if (file == NULL) {
printf("ไม่สามารถสร้างไฟล์ได้\n");
return 1;
}
fwrite(students, sizeof(Student), 3, file);
fclose(file);
printf("เขียนข้อมูล 3 records สำเร็จ\n");
// อ่าน binary file
file = fopen("students.dat", "rb");
if (file == NULL) {
printf("ไม่สามารถเปิดไฟล์ได้\n");
return 1;
}
Student read_students[3];
fread(read_students, sizeof(Student), 3, file);
fclose(file);
printf("\nข้อมูลที่อ่านได้:\n");
for (int i = 0; i < 3; i++) {
printf("ID: %d, ชื่อ: %s, คะแนน: %.1f\n",
read_students[i].id,
read_students[i].name,
read_students[i].score);
}
return 0;
}
🎯 Binary vs Text Files:
- Text: อ่านได้โดยมนุษย์, ขนาดใหญ่กว่า
- Binary: เร็วกว่า, ขนาดเล็กกว่า, อ่านไม่ได้โดยตรง
💡 Performance Tip: ใช้ binary files สำหรับข้อมูลจำนวนมากเพื่อประสิทธิภาพที่ดีกว่า
การตรวจสอบและจัดการ Errors
ตัวอย่างที่ 4: Error Handling
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void) {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
// แสดง error message
printf("Error: %s\n", strerror(errno));
perror("fopen");
return 1;
}
// ตรวจสอบ EOF และ error
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
if (ferror(file)) {
printf("เกิด error ขณะอ่านไฟล์\n");
} else if (feof(file)) {
printf("อ่านไฟล์จนจบแล้ว\n");
}
fclose(file);
return 0;
}
📌 Functions สำหรับตรวจสอบ:
- ferror() – ตรวจสอบว่าเกิด error หรือไม่
- feof() – ตรวจสอบว่าถึงจุดจบไฟล์หรือไม่
- clearerr() – ล้าง error flags
Preprocessor Directives
Preprocessor ทำงานก่อนการ compile โดยประมวลผลคำสั่งที่ขึ้นต้นด้วย #
#include - รวมไฟล์
#include <stdio.h> // ไฟล์ระบบ (system headers)
#include “myheader.h” // ไฟล์ของเรา (local headers)
💡 ความแตกต่าง:
< > – หาใน system include directories
” ” – หาใน current directory ก่อน แล้วค่อยหาใน system
#define - Macros
Simple Macros
#define MAX_SIZE 100
#define GREETING "สวัสดี"
int main(void) {
printf("PI = %f\n", PI);
int array[MAX_SIZE];
printf("%s\n", GREETING);
return 0;
}
Function-like Macros
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int main(void) {
printf("SQUARE(5) = %d\n", SQUARE(5)); // 25
printf("MAX(10, 20) = %d\n", MAX(10, 20)); // 20
printf("MIN(10, 20) = %d\n", MIN(10, 20)); // 10
// ⚠️ ระวังเรื่อง side effects
int x = 5;
printf("SQUARE(x++) = %d\n", SQUARE(x++)); // ผลลัพธ์ไม่คาดคิด!
return 0;
}
⚠️ คำเตือน Macros:
ใส่วงเล็บครอบทุก parameter เสมอ
ระวัง side effects (เช่น ++, –)
ไม่มี type checking
🎯 Best Practice: ใช้ inline functions แทน macros เมื่อเป็นไปได้ (C99 ขึ้นไป)
Conditional Compilation
#ifdef, #ifndef, #endif
ตัวอย่างที่ 5: Conditional Compilation
// กำหนด debugging mode
#define DEBUG
#define MAX_BUFFER 1024
int main(void) {
#ifdef DEBUG
printf("โหมด Debug เปิดอยู่\n");
printf("Buffer size: %d\n", MAX_BUFFER);
#endif
int result = 10 + 20;
#ifdef DEBUG
printf("Calculation: 10 + 20 = %d\n", result);
#endif
printf("ผลลัพธ์: %d\n", result);
#ifndef RELEASE
printf("นี่ไม่ใช่ release version\n");
#endif
return 0;
}
💡 Use Cases:
- Debug vs Release builds
- Platform-specific code
- Feature flags
- ป้องกัน multiple inclusion
Include Guards
ป้องกันการ include ซ้ำ
// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
// ประกาศ functions และ constants
void myFunction(void);
#define MY_CONSTANT 42
#endif // MYHEADER_H
หรือใช้ #pragma once (modern):
// myheader.h
#pragma once
void myFunction(void);
#define MY_CONSTANT 42
🎯 ทำไมต้องใช้: ป้องกัน redefinition errors เมื่อ include header file หลายครั้ง
Predefined Macros
C มี macros ที่กำหนดไว้แล้ว:
int main(void) {
printf("File: %s\n", __FILE__);
printf("Line: %d\n", __LINE__);
printf("Date: %s\n", __DATE__);
printf("Time: %s\n", __TIME__);
printf("Function: %s\n", __func__);
return 0;
}
ผลลัพธ์:
File: main.c
Line: 5
Date: Dec 10 2024
Time: 14:30:25
Function: main
💡 Use Cases: Debugging, logging, error messages
การสร้าง Header Files
mymath.h
#ifndef MYMATH_H
#define MYMATH_H
// ประกาศ functions
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
// Constants
#define PI 3.14159
#endif
mymath.c
#include “mymath.h”
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a – b;
}
int multiply(int a, int b) {
return a * b;
}
main.c
#include <stdio.h>
#include “mymath.h”
int main(void) {
printf(“10 + 5 = %d\n”, add(10, 5));
printf(“10 – 5 = %d\n”, subtract(10, 5));
printf(“10 * 5 = %d\n”, multiply(10, 5));
printf(“PI = %f\n”, PI);
return 0;
}
การ compile:
gcc -c mymath.c -o mymath.o
gcc -c main.c -o main.o
gcc mymath.o main.o -o program
การใช้งานร่วมกับ GlowCode
วิเคราะห์ File I/O Performance
#include <time.h>
int main(void) {
clock_t start, end;
double cpu_time;
// วัดเวลาการเขียนไฟล์
start = clock();
FILE *file = fopen("large_file.txt", "w");
if (file == NULL) return 1;
for (int i = 0; i < 100000; i++) {
fprintf(file, "บรรทัดที่ %d\n", i);
}
fclose(file);
end = clock();
cpu_time = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("เวลาที่ใช้: %f วินาที\n", cpu_time);
return 0;
}
ใช้ GlowCode เพื่อ:
- วัด I/O performance
- ตรวจสอบ file handle leaks
- วิเคราะห์ buffer usage
- เปรียบเทียบ text vs binary file performance
🎯 Performance Tips:
- ใช้ buffered I/O แทน unbuffered
- เขียนทีละ block ใหญ่ดีกว่าทีละตัวอักษร
- ใช้ binary สำหรับข้อมูลจำนวนมาก
- ปิดไฟล์ทันทีเมื่อใช้เสร็จ
แนวทางปฏิบัติที่ดีที่สุด
✅ File I/O:
- ตรวจสอบ fopen() return value เสมอ
- ปิดไฟล์ด้วย fclose() เสมอ
- ใช้ fgets() แทน gets() (ไม่ปลอดภัย)
- จัดการ errors อย่างเหมาะสม
- ใช้ binary mode สำหรับข้อมูล non-text
✅ Preprocessors:
- ใช้ UPPER_CASE สำหรับ macros
- ใส่วงเล็บครอบ macro parameters
- ใช้ include guards ใน header files
- หลีกเลี่ยง complex macros
- Comment macros ที่ซับซ้อน
❌ ไม่ควรทำ:
- ลืมปิดไฟล์
- ไม่ตรวจสอบ errors
- ใช้ gets() (deprecated)
- Macros ที่มี side effects
- Include header files ซ้ำโดยไม่มี guards
สรุป
ในบทนี้เราได้เรียนรู้:
✅ การอ่านและเขียนไฟล์ (text และ binary)
✅ การจัดการ errors ในการทำงานกับไฟล์
✅ Preprocessor directives (#include, #define)
✅ Macros และ conditional compilation
✅ การสร้าง header files
✅ Predefined macros
✅ การวิเคราะห์ performance ด้วย GlowCode
ขั้นตอนต่อไป
- ฝึกสร้างโปรแกรมที่อ่าน/เขียนไฟล์
- สร้าง header files และ libraries ของคุณเอง
- วิเคราะห์ file I/O performance ด้วย GlowCode
- เตรียมพร้อมสำหรับ: Dynamic Memory Allocation
เชื่อมโยงไปยังหัวข้อถัดไป: File I/O มักใช้ร่วมกับ dynamic memory allocation สำหรับอ่านไฟล์ขนาดใหญ่ที่ไม่ทราบขนาดล่วงหน้า

