C語言 open 函式完整指南|使用方法、旗標與錯誤處理徹底解析

目次

1. C 語言的open函數是什麼?

1.1 open函數的作用

C語言的open函數是用來開啟或建立檔案的系統呼叫。 一般而言,在 Linux 或 UNIX 系統環境中使用,當需要執行比標準函式庫的fopen更低階的檔案操作時會被使用。

1.2 fopen函數的差異

C語言中也有用於開啟檔案的標準函式庫函數fopen,但其用途與open函數不同。
函式特徵
open低階 API、回傳檔案描述符、系統呼叫
fopen高階 API、回傳FILE*、支援緩衝
open函數的用途
  • 記錄日誌檔案(使用 O_APPEND
  • 建立暫存檔案(O_TMPFILE
  • 非同步處理(O_NONBLOCK
  • 建立具有特定存取權限的檔案(O_CREAT + mode
如此,open函數可用於從簡單的檔案操作到高階的檔案管理,應用範圍廣泛。

2. open函式的基本使用方式【附範例程式碼】

2.1 open函式的原型

open函式在 fcntl.h 標頭檔中定義,以下如所示。
#include <fcntl.h>

int open(const char *path, int flags, mode_t mode);
  • path:要開啟的檔案路徑(例如:"sample.txt")。
  • flags:指定檔案開啟模式與行為的旗標(例如:O_RDONLY)。
  • mode:檔案建立時的權限(在指定 O_CREAT 時需要)。
  • 返回值:
  • 成功時:檔案描述符(0 以上的整數)
  • 失敗時:返回 -1,錯誤資訊存於 errno

2.2 open函式的基本使用方式

以下範例示範以讀寫模式(O_RDWR)開啟檔案,若需新建則以 0644 權限建立。
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("sample.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    printf("File opened successfully with descriptor %dn", fd);

    close(fd);
    return 0;
}

2.3 open函式的錯誤處理

open函式失敗時,會返回 -1,錯誤代碼會存於全域變數 errno。 使用 perror() 可以清楚顯示錯誤內容。
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main() {
    int fd = open("/root/protected.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    close(fd);
    return 0;
}
錯誤代碼範例:
  • EACCES(Permission denied):權限不足
  • ENOENT(No such file or directory):指定的檔案不存在

2.4 open函式的選項活用

  • 以唯讀方式開啟
  int fd = open("sample.txt", O_RDONLY);
  • 以唯寫方式開啟
  int fd = open("sample.txt", O_WRONLY);
  • 檔案不存在時新建
  int fd = open("sample.txt", O_WRONLY | O_CREAT, 0644);
  • 開啟既有檔案並清空內容
  int fd = open("sample.txt", O_WRONLY | O_TRUNC);

3. open函式的旗標列表【完整指南】

3.1 基本的旗標

3.1.1 指定讀寫模式的旗標

旗標說明
O_RDONLY以唯讀方式開啟
O_WRONLY以唯寫方式開啟
O_RDWR以可讀寫的模式開啟

3.1.2 使用範例

int fd1 = open("file.txt", O_RDONLY);  // 以唯讀方式
int fd2 = open("file.txt", O_WRONLY);  // 以唯寫方式
int fd3 = open("file.txt", O_RDWR);    // 可讀寫

3.2 關於檔案建立與管理的旗標

3.2.1 O_CREAT(檔案不存在時新建)

int fd = open("newfile.txt", O_WRONLY | O_CREAT, 0644);

3.2.2 O_EXCL(與 O_CREAT 同時使用,若檔案已存在則錯誤)

int fd = open("newfile.txt", O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd == -1) {
    perror("File already exists");
}

3.2.3 O_TRUNC(開啟已存在的檔案時刪除內容)

int fd = open("log.txt", O_WRONLY | O_TRUNC);

3.3 控制寫入行為的旗標

3.3.1 O_APPEND(以追加模式開啟)

int fd = open("log.txt", O_WRONLY | O_APPEND);
write(fd, "New log entryn", 14);

3.4 控制非同步/特殊行為的旗標

3.4.1 O_NONBLOCK(非阻塞模式)

int fd = open("fifo_pipe", O_RDONLY | O_NONBLOCK);

3.4.2 O_SYNC(同步寫入)

int fd = open("important_data.txt", O_WRONLY | O_SYNC);

3.4.3 O_NOFOLLOW(不追蹤符號連結)

int fd = open("symlink_file", O_RDONLY | O_NOFOLLOW);

3.5 旗標的組合

目的旗標的組合
可讀寫 + 新建O_RDWR | O_CREAT, 0644
唯寫 + 追加模式O_WRONLY | O_APPEND
唯讀 + 非同步處理O_RDONLY | O_NONBLOCK
開啟檔案並清空內容O_WRONLY | O_TRUNC
僅在檔案不存在時新建O_WRONLY | O_CREAT | O_EXCL, 0644

4. mode(檔案權限)的詳細

4.1 mode是什麼?

mode是,在使用 open 函式建立新檔案時(使用 O_CREAT 標誌),用來指定檔案存取權限的值。
int fd = open("newfile.txt", O_WRONLY | O_CREAT, 0644);

4.2 mode的基本設定

權限值存取權限意義
0(無)---不可存取
1(執行)--x僅執行
2(寫入)-w-僅寫入
4(讀取)r--僅讀取
6(讀取+寫入)rw-讀取與寫入
7(全部允許)rwx讀取、寫入、執行

4.3 modeumask的關係

在 UNIX 系統中,mode的指定值不會直接套用,而是受到 umask(使用者遮罩)的影響。
0666(指定的mode)
- 0022(umask)
------------
0644(實際套用的權限)
確認目前的 umask
$ umask
0022

4.4 使用 chmod 變更權限

如果想在建立後變更權限,請使用 chmod 指令。
$ chmod 600 secret.txt  # 秘密檔案
$ chmod 755 script.sh   # 可執行檔案
在 C 語言中使用 chmod 時:
#include <sys/stat.h>

chmod("file.txt", 0644);

4.5 考慮 modeopen 函式實作範例

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("secure.txt", O_WRONLY | O_CREAT, 0600);
    if (fd == -1) {
        perror("開啟失敗");
        return 1;
    }
    printf("File created successfully with descriptor %dn", fd);
    close(fd);
    return 0;
}

5. open函式與相關的系統呼叫

5.1 close函式(關閉檔案)

5.1.1 close的原型

#include <unistd.h>

int close(int fd);

5.1.2 close的使用範例

#include <fcntl.h>#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("sample.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    printf("File opened successfully.n");

    if (close(fd) == -1) {
        perror("close failed");
        return 1;
    }

    printf("File closed successfully.n");

    return 0;
}

5.2 read函式

6. open 函式的實務使用範例

6.1 以追加模式開啟記錄檔

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    const char *log_entry = "New log entryn";
    write(fd, log_entry, 14);

    close(fd);
    return 0;
}

6.2 建立暫存檔

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("/tmp", O_TMPFILE | O_RDWR, 0644);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    const char *data = "Temporary datan";
    write(fd, data, 15);

    printf("Temporary file created (fd = %d)n", fd);

    close(fd);
    return 0;
}

6.3 使用 O_NONBLOCK 的非同步處理

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("fifo_pipe", O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    printf("File opened in non-blocking moden");

    close(fd);
    return 0;
}

6.4 錯誤處理的實作

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main() {
    int fd = open("/root/protected.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");

        if (errno == EACCES) {
            printf("Permission deniedn");
        } else if (errno == ENOENT) {
            printf("File does not existn");
        }

        return 1;
    }

    close(fd);
    return 0;
}

6.5 利用 lseek 變更檔案位置

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("sample.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    lseek(fd, 10, SEEK_SET);

    char buffer[11];
    read(fd, buffer, 10);
    buffer[10] = '�';

    printf("Read after seek: %sn", buffer);

    close(fd);
    return 0;
}

7. open函數相關的常見問題(FAQ)

7.1 open函數與fopen函數的差異是?

函式特徵用途
open系統呼叫,回傳檔案描述符(整數)低階檔案操作、系統程式設計
fopen標準 C 函式庫,回傳 FILE*利用緩衝的通用檔案 I/O

7.2 open函數中mode是否必須指定?

  • 僅在使用 O_CREAT 時才需要
int fd = open("newfile.txt", O_WRONLY | O_CREAT, 0644);

7.3 指定 O_APPEND 後,寫入位置是否永遠在檔案末端?

  • 是的,指定 O_APPEND 後寫入會永遠在檔案末端進行
int fd = open("log.txt", O_WRONLY | O_APPEND);
write(fd, "New entryn", 10);
close(fd);

7.4 什麼時候使用 O_NONBLOCK 旗標?

  • 在 FIFO(管道)或 socket 想要立即返回的情況
int fd = open("fifo_pipe", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
    perror("open failed");
}

7.5 用 open 函數開啟符號連結會發生什麼?

  • 通常會開啟連結指向的檔案,但使用 O_NOFOLLOW 可以防止
int fd = open("symlink.txt", O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
    perror("open failed");
}

7.6 如何使用 open 函數取得檔案大小?

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("sample.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    off_t filesize = lseek(fd, 0, SEEK_END);
    printf("File size: %ld bytesn", filesize);

    close(fd);
    return 0;
}

7.7 open 函數失敗的主要原因是?

錯誤代碼意義
EACCES沒有存取權限
ENOENT檔案不存在
EEXIST使用 O_CREAT | O_EXCL 時已有檔案存在
EMFILE超過行程可開啟檔案數量上限
ENOSPC磁碟空間不足
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main() {
    int fd = open("/root/protected.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");

        if (errno == EACCES) {
            printf("Permission deniedn");
        } else if (errno == ENOENT) {
            printf("File does not existn");
        }

        return 1;
    }

    close(fd);
    return 0;
}

8. 總結

8.1 open函式的基本

  • open函式是用來開啟或建立檔案的系統呼叫。
  • fopen 不同,會回傳檔案描述符(FD)
  • 適當執行 close(fd),以防止資源洩漏。

8.2 open函式的主要旗標

旗標說明
O_RDONLY以唯讀方式開啟
O_WRONLY以唯寫方式開啟
O_RDWR以可讀寫模式開啟
O_CREAT若檔案不存在則新建
O_TRUNC刪除已存在檔案的內容
O_APPEND寫入時總是追加至檔案尾端
O_NONBLOCK非阻塞模式
O_NOFOLLOW不開啟符號連結

8.3 open函式與相關的系統呼叫

  • close(fd):釋放檔案描述符。
  • read(fd, buf, count):從檔案讀取資料。
  • write(fd, buf, count):寫入資料至檔案。
  • lseek(fd, offset, whence):移動檔案的讀寫位置。

8.4 open函式的實務使用範例

  • 日誌檔案的追加 → 使用 O_APPEND 追加至尾端。
  • 暫存檔案的建立 → 活用 O_TMPFILE
  • 非同步處理 → 使用 O_NONBLOCK 以避免阻塞。

8.5 open函式的注意事項

  • 適當地呼叫 close(fd):為防止資源洩漏。
  • 執行錯誤處理:檢查 errno,並採取適當的對策。
  • 不要設定錯誤的權限:若 mode 指定錯誤,會造成安全風險。

8.6 本文的總結

章節內容
open函式的基本用於低階檔案操作的系統呼叫
open函式的使用方式使用 O_CREAT 時需要 mode
open的主要旗標O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, etc.
相關系統呼叫close, read, write, lseek
實務使用範例日誌記錄、暫存檔案、非同步處理、錯誤處理
常見問答fopen 的差異、O_APPEND 的運作、錯誤處理

8.7 最後

C語的open函式是系統層級的檔案操作不可或缺的系統呼叫。適當使用可實現高效的檔案管理與非同步處理。請活用本文所學的知識,開發安全且堅固的程式。
年収訴求