在Linux系統編程中,文件IO操作是非常常見和重要的操作之一。通過文件IO操作,我們可以打開、讀取、寫入和關閉文件,對文件進行定位、復制、刪除和重命名等操作。本篇博客將介紹一些常用的文件IO操作函數。
文章目錄
- 1. open()
- 1.1 原型、參數及返回值說明
- 1.1.1 原型:
- 1.1.2 參數說明:
- 1.1.3 返回值:
- 1.2 函數示例
- 1.3 代碼解釋
- 2. close()
- 2.1 原型、參數及返回值說明
- 2.1.1 原型:
- 2.1.2 參數:
- 2.1.3 返回值:
- 2.2 函數示例
- 2.3 代碼解釋
- 3. read()
- 3.1 原型、參數及返回值說明
- 3.1.1 原型:
- 3.1.2 參數:
- 3.1.3 返回值:
- 3.2 函數示例
- 3.3 代碼解釋
- 4. write()
- 4.1 原型、參數及返回值說明
- 4.1.1 原型:
- 4.1.2 參數:
- 4.1.3 返回值:
- 4.2 函數示例
- 4.3 代碼解釋
- 5. lseek()
- 5.1 原型、參數及返回值說明
- 5.1.1 原型:
- 5.1.2 參數:
- 5.1.3 返回值:
- 5.2 函數示例
- 5.3 代碼解釋
- 6. stat()
- 6.1 原型、參數及返回值說明
- 6.1.1 原型:
- 6.1.2 參數:
- 6.1.3 返回值:
- 6.2 函數示例
- 6.3 代碼解釋
- 7. fcntl()
- 7.1 原型、參數及返回值說明
- 7.1.1 原型:
- 7.1.2 參數:
- 7.1.3 返回值:
- 7.2 函數示例
- 7.3 代碼解釋
- 總結
1. open()
1.1 原型、參數及返回值說明
1.1.1 原型:
open()函數是Linux系統編程中常用的文件IO操作函數之一。它用于打開文件并返回一個文件描述符,以便后續的文件讀寫操作。
open()函數的原型如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags, mode_t mode);
1.1.2 參數說明:
pathname
:要打開的文件路徑名。flags
:打開文件的模式標志,可以是以下幾種模式的組合:O_RDONLY
:只讀模式打開文件。O_WRONLY
:只寫模式打開文件。O_RDWR
:讀寫模式打開文件。O_CREAT
:如果文件不存在,則創建文件。O_EXCL
:與O_CREAT一同使用,如果文件已存在,則返回錯誤。O_TRUNC
:如果文件存在且以可寫模式打開,則將文件截斷為0。O_APPEND
:在文件末尾追加數據。
mode
: 創建文件時的訪問權限,只有在使用O_CREAT時才有效。
可以使用如下幾個宏來設置權限:- S_IRUSR:用戶讀權限。
- S_IWUSR:用戶寫權限。
- S_IXUSR:用戶執行權限。
- S_IRGRP:組讀權限。
- S_IWGRP:組寫權限。
- S_IXGRP:組執行權限。
- S_IROTH:其他用戶讀權限。
- S_IWOTH:其他用戶寫權限。
- S_IXOTH:其他用戶執行權限。
1.1.3 返回值:
- 成功:返回一個非負整數的文件描述符,用于后續的文件IO操作。
- 失敗:返回-1,并設置errno變量來指示錯誤類型。
1.2 函數示例
下面是一個使用open()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>int main() {int fd;// 打開文件fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);if (fd == -1) {perror("open");return errno;}// 寫入數據char buffer[] = "Hello, world!";ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);if (ret == -1) {perror("write");close(fd);return errno;}// 關閉文件close(fd);return 0;
}
1.3 代碼解釋
在上述示例中,我們首先使用open()函數以只寫模式打開一個文件example.txt。如果文件不存在,則創建該文件,并設置訪問權限為用戶可讀可寫。如果打開文件失敗,我們使用perror()函數打印錯誤信息,并返回errno變量。
接下來,我們使用write()函數向文件中寫入數據。在本例中,我們寫入了字符串"Hello, world!"。如果寫入數據失敗,我們同樣使用perror()函數打印錯誤信息,并在關閉文件前返回errno變量。
最后,我們使用close()函數關閉文件。
2. close()
2.1 原型、參數及返回值說明
2.1.1 原型:
close()函數是Linux系統編程中用于關閉文件的函數。它接受一個文件描述符作為參數,并返回一個整數值來指示操作是否成功。
close()函數的原型如下:
#include <unistd.h>int close(int fd);
2.1.2 參數:
fd
:要關閉的文件描述符。
2.1.3 返回值:
- 成功:返回0。
- 失敗:返回-1,并設置
errno
變量來指示錯誤類型。
2.2 函數示例
下面是一個使用close()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>int main() {int fd;// 打開文件fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return errno;}// 關閉文件int ret = close(fd);if (ret == -1) {perror("close");return errno;}return 0;
}
2.3 代碼解釋
在上述示例中,我們首先使用open()
函數以只讀模式打開一個文件example.txt
,并將返回的文件描述符存儲在變量fd
中。如果打開文件失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
接下來,我們使用close()
函數關閉文件。如果關閉文件失敗,我們同樣使用perror()
函數打印錯誤信息,并返回errno
變量。
需要注意的是,關閉文件后,我們不應再對該文件描述符進行任何操作。
3. read()
3.1 原型、參數及返回值說明
3.1.1 原型:
read()函數是Linux系統編程中用于從文件中讀取數據的函數。它接受一個文件描述符、一個緩沖區地址和一個讀取的最大字節數作為參數,并返回實際讀取的字節數。
read()函數的原型如下:
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
3.1.2 參數:
fd
:要讀取的文件的文件描述符。buf
:用于存儲讀取數據的緩沖區的地址。count
:要讀取的最大字節數。
3.1.3 返回值:
- 成功:返回實際讀取的字節數。
- 失敗:返回
-1
,并設置errno
變量來指示錯誤類型。
3.2 函數示例
下面是一個使用read()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>int main() {int fd;// 打開文件fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return errno;}// 讀取數據char buffer[100];ssize_t ret = read(fd, buffer, sizeof(buffer));if (ret == -1) {perror("read");close(fd);return errno;}// 輸出讀取的數據printf("Read %ld bytes: %s\n", ret, buffer);// 關閉文件close(fd);return 0;
}
3.3 代碼解釋
在上述示例中,我們首先使用open()
函數以只讀模式打開一個文件example.txt
,并將返回的文件描述符存儲在變量fd
中。如果打開文件失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
接下來,我們使用read()
函數從文件中讀取數據。我們定義一個長度為100的緩沖區buffer
,并將其作為參數傳遞給read()
函數。read()
函數將盡量讀取count
個字節的數據,并將其存儲在緩沖區中。如果讀取數據失敗,我們同樣使用perror()
函數打印錯誤信息,并在關閉文件前返回errno
變量。
最后,我們使用printf()
函數輸出讀取的數據,并使用close()
函數關閉文件。
需要注意的是,read()函數是一個阻塞函數,如果文件中沒有足夠的數據可讀,它將一直等待直到有足夠的數據可讀或者發生錯誤。如果需要非阻塞地讀取數據,可以使用fcntl()函數設置文件描述符為非阻塞模式。
4. write()
4.1 原型、參數及返回值說明
4.1.1 原型:
write()函數是Linux系統編程中用于向文件中寫入數據的函數。它接受一個文件描述符、一個數據緩沖區地址和要寫入的字節數作為參數,并返回實際寫入的字節數。
write()函數的原型如下:
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);
4.1.2 參數:
fd
:要寫入的文件的文件描述符。buf
:要寫入的數據的緩沖區的地址。count
:要寫入的字節數。
4.1.3 返回值:
- 成功:返回實際寫入的字節數。
- 失敗:返回
-1
,并設置errno
變量來指示錯誤類型。
4.2 函數示例
下面是一個使用write()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>int main() {int fd;// 打開文件fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);if (fd == -1) {perror("open");return errno;}// 寫入數據char buffer[] = "Hello, World!";ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);if (ret == -1) {perror("write");close(fd);return errno;}// 關閉文件close(fd);return 0;
}
4.3 代碼解釋
在上述示例中,我們首先使用open()
函數以只寫模式打開一個文件example.txt
,并將返回的文件描述符存儲在變量fd
中。如果打開文件失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
接下來,我們使用write()
函數將數據寫入文件中。我們定義一個字符串buffer
,并將其作為參數傳遞給write()
函數。write()
函數將盡量寫入count
個字節的數據到文件中。如果寫入數據失敗,我們同樣使用perror()
函數打印錯誤信息,并在關閉文件前返回errno
變量。
最后,我們使用close()
函數關閉文件。
需要注意的是,write()函數是一個阻塞函數,如果文件無法立即接受寫入的數據(例如,磁盤空間不足),它將一直等待直到可以寫入數據或者發生錯誤。如果需要非阻塞地寫入數據,可以使用fcntl()函數設置文件描述符為非阻塞模式。
5. lseek()
5.1 原型、參數及返回值說明
5.1.1 原型:
lseek()函數是Linux系統編程中用于在文件中定位讀寫位置的函數。它接受一個文件描述符、一個偏移量和一個起始位置作為參數,并返回新的讀寫位置。
lseek()函數的原型如下:
#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);
5.1.2 參數:
fd
:要定位的文件的文件描述符。offset
:偏移量,可以是正數、負數或零。whence
:起始位置,可以取以下三個值:SEEK_SET
:從文件開頭開始計算偏移量。SEEK_CUR
:從當前讀寫位置開始計算偏移量。SEEK_END
:從文件末尾開始計算偏移量。
5.1.3 返回值:
- 成功:返回新的讀寫位置。
- 失敗:返回
-1
,并設置errno
變量來指示錯誤類型。
5.2 函數示例
下面是一個使用lseek()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>int main() {int fd;// 打開文件fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return errno;}// 定位讀寫位置off_t ret = lseek(fd, 5, SEEK_SET);if (ret == -1) {perror("lseek");close(fd);return errno;}// 讀取數據char buffer[10];ssize_t n = read(fd, buffer, sizeof(buffer) - 1);if (n == -1) {perror("read");close(fd);return errno;}buffer[n] = '\0';printf("Data: %s\n", buffer);// 關閉文件close(fd);return 0;
}
5.3 代碼解釋
在上述示例中,我們首先使用open()
函數以只讀模式打開一個文件example.txt
,并將返回的文件描述符存儲在變量fd
中。如果打開文件失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
接下來,我們使用lseek()
函數將讀寫位置定位到文件開頭后的第5個字節。我們將文件描述符、偏移量和起始位置作為參數傳遞給lseek()
函數。如果定位讀寫位置失敗,我們同樣使用perror()
函數打印錯誤信息,并在關閉文件前返回errno
變量。
然后,我們使用read()
函數從文件中讀取數據。我們定義一個緩沖區buffer
,并將其作為參數傳遞給read()
函數。read()
函數將盡量讀取count
個字節的數據到緩沖區中。如果讀取數據失敗,我們同樣使用perror()
函數打印錯誤信息,并在關閉文件前返回errno
變量。最后,我們在緩沖區末尾添加一個空字符,并使用printf()
函數打印讀取到的數據。
最后,我們使用close()
函數關閉文件。
需要注意的是,lseek()函數可以用于定位讀寫位置,但并不會改變文件的大小。如果需要改變文件的大小,可以使用truncate()函數或ftruncate()函數。
6. stat()
6.1 原型、參數及返回值說明
6.1.1 原型:
stat()函數是Linux系統編程中用于獲取文件信息的函數。它接受一個文件路徑作為參數,并返回一個包含文件信息的結構體。
stat()函數的原型如下:
#include <sys/types.h>
#include <sys/stat.h>int stat(const char *pathname, struct stat *buf);
6.1.2 參數:
pathname
:要獲取信息的文件路徑。buf
:用于存儲文件信息的結構體指針。
6.1.3 返回值:
- 成功:返回0。
- 失敗:返回
-1
,并設置errno
變量來指示錯誤類型。
struct stat
結構體包含了文件的各種信息,包括文件類型、權限、大小、創建時間、修改時間等。
下面是struct stat結構體的定義:
struct stat {dev_t st_dev; // 文件所在設備的IDino_t st_ino; // 文件的inode號mode_t st_mode; // 文件的類型和權限nlink_t st_nlink; // 文件的硬鏈接數uid_t st_uid; // 文件的所有者IDgid_t st_gid; // 文件的所有者組IDdev_t st_rdev; // 如果文件是設備文件,則為設備的IDoff_t st_size; // 文件的大小(字節)blksize_t st_blksize; // 文件系統的塊大小blkcnt_t st_blocks; // 分配給文件的塊數time_t st_atime; // 文件的最后訪問時間time_t st_mtime; // 文件的最后修改時間time_t st_ctime; // 文件的最后狀態改變時間
};
6.2 函數示例
下面是一個使用stat()函數的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>int main() {const char *pathname = "example.txt";struct stat file_info;// 獲取文件信息int ret = stat(pathname, &file_info);if (ret == -1) {perror("stat");return errno;}// 打印文件信息printf("File Size: %ld bytes\n", file_info.st_size);printf("File Permissions: %o\n", file_info.st_mode & 0777);printf("File Owner UID: %d\n", file_info.st_uid);printf("File Owner GID: %d\n", file_info.st_gid);return 0;
}
6.3 代碼解釋
在上述示例中,我們首先定義了一個文件路徑pathname
和一個struct stat
結構體file_info
,用于存儲獲取到的文件信息。
然后,我們使用stat()
函數將文件信息存儲到file_info
結構體中。我們將文件路徑和file_info
結構體指針作為參數傳遞給stat()
函數。如果獲取文件信息失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
最后,我們使用printf()
函數打印獲取到的文件信息,包括文件大小、文件權限、文件所有者的UID
和GID
等。
需要注意的是,stat()函數只能獲取文件的信息,而不能修改文件的信息。如果需要修改文件的信息,可以使用chmod()函數來修改文件的權限。
7. fcntl()
7.1 原型、參數及返回值說明
7.1.1 原型:
fcntl()函數是Linux系統編程中用于對文件描述符進行控制操作的函數。它可以用于設置文件狀態標志、獲取文件狀態標志、設置文件鎖等。
fcntl()函數的原型如下:
#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );
7.1.2 參數:
fd
:文件描述符,可以是打開文件的文件描述符,也可以是套接字的文件描述符。cmd
:控制命令,用于指定要執行的操作。arg
:可選參數,用于傳遞特定操作的參數。
7.1.3 返回值:
- 成功:根據不同的操作命令返回不同的值,一般為0或一個正整數。
- 失敗:返回
-1
,并設置errno
變量來指示錯誤類型。
下面是fcntl()函數的一些常用命令:
F_DUPFD
:復制文件描述符,創建一個新的文件描述符,該描述符與原始描述符指向相同的打開文件。F_GETFD
:獲取文件描述符的文件狀態標志。F_SETFD
:設置文件描述符的文件狀態標志。F_GETFL
:獲取文件的打開方式和狀態標志。F_SETFL
:設置文件的打開方式和狀態標志。F_GETLK
:獲取文件鎖的信息。F_SETLK
:設置文件鎖。F_SETLKW
:設置文件鎖,如果無法獲取鎖,則阻塞等待。
7.2 函數示例
下面是一個使用fcntl()函數的示例:
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>int main() {int fd = open("example.txt", O_RDWR);if (fd == -1) {perror("open");return errno;}// 獲取文件的打開方式和狀態標志int flags = fcntl(fd, F_GETFL);if (flags == -1) {perror("fcntl");return errno;}// 設置文件的狀態標志為非阻塞flags |= O_NONBLOCK;int ret = fcntl(fd, F_SETFL, flags);if (ret == -1) {perror("fcntl");return errno;}// 關閉文件描述符close(fd);return 0;
}
7.3 代碼解釋
在上述示例中,我們首先使用open()
函數打開一個文件,并將返回的文件描述符存儲在變量fd中。如果打開文件失敗,我們使用perror()
函數打印錯誤信息,并返回errno
變量。
然后,我們使用fcntl()
函數獲取文件的打開方式和狀態標志。將文件描述符和F_GETFL
命令作為參數傳遞給fcntl()
函數,獲取到的文件狀態標志存儲在變量flags
中。如果獲取文件狀態標志失敗,我們同樣使用perror()
函數打印錯誤信息,并返回errno
變量。
接下來,我們將文件的狀態標志設置為非阻塞,通過將O_NONBLOCK
標志位與flags
進行按位或操作。然后,我們使用fcntl()
函數將修改后的狀態標志設置回文件描述符。將文件描述符、F_SETFL
命令和修改后的狀態標志作為參數傳遞給fcntl()
函數。如果設置文件狀態標志失敗,我們同樣使用perror()
函數打印錯誤信息,并返回errno
變量。
最后,我們使用close()
函數關閉文件描述符,釋放資源。
需要注意的是,fcntl()函數的使用非常靈活,可以根據需要進行各種操作,如復制文件描述符、獲取文件鎖等。在使用fcntl()函數時,需要注意錯誤處理,并確保文件描述符的有效性。同時,需要在不再需要使用文件描述符時及時關閉文件描述符,以釋放資源并避免資源泄漏。
總結
文件IO操作是Linux系統編程中的重要部分。通過open()
、close()
、read()
、write()
等函數,我們可以對文件進行打開、讀取和寫入操作。通過lseek()
函數,我們可以在文件中進行定位。而通過stat()
、fcntl()
、dup()
等函數,我們可以獲取文件的狀態信息,對文件描述符進行控制操作,復制文件描述符等。
除了上述的函數,還有許多其他的函數可以用于文件IO操作,如mkdir()
用于創建目錄,rmdir()
用于刪除目錄,unlink()
用于刪除文件,rename()
用于重命名文件等。