---
# System prepended metadata

title: C Language / File Processing
tags: [C Language]

---

# C Language / File Processing


## 序章 / 檔案的概念與重要性

- 儲存在變數和陣列中的資料是暫時性的，程式執行結束後這些資料就會消失。
- 檔案用於永久保存資料。
- 電腦會將檔案儲存在次要儲存裝置上，如硬碟、光碟和隨身碟等。
- 本章將解釋 C 程式如何建立、更新和處理資料檔案，包括循序存取（sequential-access）和隨機存取（random-access）。
- ~~寫這篇的時候差點以為自己讀一類（X~~

## Ch01 / 資料流與檔案串流

- C 語言將每個檔案視為一個有序的位元組流。
- 每個檔案以檔案結束標記或系統維護的管理資料結構中記錄的特定位元組數目作為結尾。
- 開啟檔案時，系統會為其建立一個相關聯的資料流。
- 程式執行時，標準輸入（standard input）、標準輸出（standard output）和標準錯誤（standard error）這三個檔案及其關聯的資料流會自動開啟。
- 標準輸入資料流允許程式從鍵盤讀取資料。
- 標準輸出資料流允許程式將資料輸出到螢幕上。

## Ch02 / 檔案指標與檔案結構

- 開啟檔案時，會傳回一個指向 `FILE` 結構（定義於`<stdio.h>`）的指標，該結構包含用於處理檔案的資訊。
    - 在某些系統上，這個結構可能包含一個檔案描述符，用作開啟檔案表的索引。
    - 開啟檔案表的每個元素都包含一個檔案控制區塊（FCB），作為作業系統管理特定檔案所用。
- 標準輸入、標準輸出和標準錯誤分別使用檔案指標 `stdin`、`stdout` 和 `stderr` 進行操作。

**e.g.**
```c
fputc('a', stdout);
```
我們將字元 `'a'` 寫入 `stdout`（標準輸出），這個呼叫等同於 `putchar('a')`。

## Ch03 / 讀寫檔案的標準函式庫函數

- 標準函式庫提供了許多用於讀取檔案和寫入檔案的函數。
    - `fgetc` 函數類似 `getchar` 函數，用於從檔案中讀取單個字元。它需要一個 `FILE` 指標作為參數，以指定要從哪個檔案中讀取字元。
    - `fgetc(stdin)` 會從 `stdin`（標準輸入）中讀取一個字元，等同於 `getchar()` 呼叫。
    - `fputc` 函數類似 `putchar` 函數，用於將單個字元寫入檔案。它需要一個字元值和一個 `FILE` 指標作為參數，以指定要將字元寫入哪個檔案。
- 還有其他一些函數，如 `fgets` 和 `fputs` 可用於從檔案讀取或寫入一行資料。`fscanf` 和 `fprintf` 則分別對應於 `scanf` 和 `printf`，用於從檔案讀取或寫入格式化的資料。

## Ch04 / 檔案紀錄的處理方式

- C 語言中沒有檔案紀錄的概念，但泥可以自行對檔案施加紀錄結構。
- 下面範例會說明 `FILE` 指標、`FILE` 結構和 FCB 之間的關係。當開啟名為 "clients_data" 的檔案時，該檔案的 FCB 會被複製到記憶體中，而 `fopen` 所傳回的檔案指標會與作業系統用於管理該檔案的 FCB 建立連接。

## Ch05 / 實作方式說明

### 定義
```c=
FILE *file;
fopen(const char *filename, const char *mode);
```
fopen() 函數會開啟由檔名指定的檔案。 mode 參數是一個字串，指定針對檔案所要求的存取類型，其中會包含一個**位置參數**，後面緊接著**關鍵字參數**（optional）。

### 常用位置參數說明

| 位置參數 | 說明 |
| --- | --- |
| `r` | 開啟文字檔以進行讀取。 檔案必須存在。 |
| `w` | 建立要寫入的文字檔。 如果給定檔案存在，則除非它是邏輯檔案，否則會毀損其內容。 |
| `A` | 以附加模式開啟文字檔，以在檔案結尾寫入。 如果檔案不存在且不是邏輯檔案，則 fopen() 函數會建立該檔案ext |
| `r +` | 開啟文字檔以進行讀取及寫入。 檔案必須存在。|
| `w +` | 建立用於讀取及寫入的文字檔。 如果給定的檔案存在，則除非它是邏輯檔案，否則會清除其內容。|
| `a +` | 以附加模式開啟文字檔，以在檔案結尾讀取或更新。 如果檔案不存在， fopen() 函數會建立該檔案。|
| `RB` | 開啟二進位檔以進行讀取。 檔案必須存在|




## Ch06 / 檔案紀錄的處理方式

### 範例一：建立簡易的循序存取檔案

```c=
/* 初始化檔案 clients_data */
#include <stdio.h>

int main(void)
{ 
   FILE *cfPtr;  /* clients_data 檔案的指標 */

   /* fopen 開啟檔案，如果檔案不存在則建立新檔案 */
   if ((cfPtr = fopen("clients_data", "w")) == NULL) {
      puts("檔案無法開啟");
   } 
   else {
      /* 輸出資料到檔案 */
      fprintf(cfPtr, "%-6d%-16s%-11s%10.2f\n", 1, "Jones", "Chloe", 10.98);
      fprintf(cfPtr, "%-6d%-16s%-11s%10.2f\n", 2, "Smith", "Ophelia", 572.36);
      fprintf(cfPtr, "%-6d%-16s%-11s%10.2f\n", 3, "Brown", "Sabina", 9198.32);
      fprintf(cfPtr, "%-6d%-16s%-11s%10.2f\n", 4, "Allen", "Mariah", 7.23);
       
      /* 關閉檔案 */
      fclose(cfPtr); 
   }
}
```

程式會建立一個名為 "clients_data" 的檔案，其中包含四筆客戶資料，每一筆客戶資料包含帳號、姓名和應付餘額等資訊。

### 範例二：讀取循序存取檔案

```c=
#include <stdio.h>

int main(void)
{ 
   FILE *cfPtr;  /* clients_data 檔案的指標 */
   int accountNumber; 
   char lastName[16];
   char firstName[11]; 
   float balance;

   /* fopen 開啟檔案進行讀取 */
   cfPtr = fopen("clients.dat", "r");

   if (cfPtr == NULL) {
      printf("檔案無法開啟\n");
   }
   else {
      printf("%-6s%-16s%-11s%10s\n", "帳號", "姓氏", "名字", "餘額");

      /* 讀取檔案內容並輸出 */
      while (!feof(cfPtr)) {
         fscanf(cfPtr, "%d%15s%10s%f", &accountNumber,
                lastName, firstName, &balance);
         printf("%-6d%-16s%-11s%10.2f\n", accountNumber,
                lastName, firstName, balance);
      } 

      fclose(cfPtr); 
   }
}
```

程式會開啟先前創建的 "clients_data" 檔案，讀取和顯示其中儲存的客戶。

### 範例三：Uva 00118 - Mutant Flatworld Explorers

```c=
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define MAX_SIZE 100

int main()
{
    FILE *fp = fopen("robot_in.txt", "r");
    if (fp == NULL)
    {
        printf("Error opening file\n");
        return 1;
    }

    int set;
    fscanf(fp, "%d", &set);
    int x_margin, y_margin;
    fscanf(fp, "%d", &x_margin);
    fscanf(fp, "%d", &y_margin);

    int arr[MAX_SIZE][MAX_SIZE] = {0};
    int dx[] = {0, 1, 0, -1};
    int dy[] = {1, 0, -1, 0};

    char map1[] = "NESW";
    int map2[] = {0, 1, 2, 3};

    while (set-- > 0)
    {
        int x, y, D;
        char direction;
        char move[MAX_SIZE];

        fscanf(fp, "%d %d %c", &x, &y, &direction);
        D = map2[strchr(map1, direction) - map1];
        fgets(move, MAX_SIZE, fp);
        fgets(move, MAX_SIZE, fp);

        int current_x = 0, current_y = 0;
        bool lost = false;

        for (int i = 0; move[i]; i++)
        {
            lost = false;
            if (move[i] == 'F')
            {
                current_x = x + dx[D];
                current_y = y + dy[D];
                if (current_x >= 0 && current_x <= x_margin && current_y >= 0 && current_y <= y_margin)
                {
                    x = current_x;
                    y = current_y;
                }
                else
                {
                    if (arr[x][y] == 1)
                    {
                        continue;
                    }
                    else
                    {
                        lost = true;
                        arr[x][y] = 1;
                        printf("%d %d %c LOST\n", x, y, map1[D]);
                        break;
                    }
                }
            }
            else if (move[i] == 'R')
            {
                D = (D + 1) % 4;
            }
            else if (move[i] == 'L')
            {
                D = (D + 3) % 4;
            }
        }

        if (!lost)
        {
            printf("%d %d %c\n", x, y, map1[D]);
        }
    }

    fclose(fp);
    return 0;
}
```

**robot_in.txt：**
```
3
5 3
1 1 E
RFRFRFRF
3 2 N
FRRFLLFFRRFLL
0 3 W
LLFFFLFLFL
```

---
*註: 部分內容截自於網路，此筆記非完全原創。*  
***Latest Updated On：2024.06.05,   
published with the of LICENSE of WTFPL  
Author：Qaron（呱呱）***