Linux程式設計入門(3)-檔案讀寫操作(cp指令實現)
學習目標
通過分析 cp 指令,來學習 Linux 程式設計讀寫檔案操作。
程式碼實驗環境
作業系統:Ubuntu 18.04 LTS
編譯器gcc版本:gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
cp指令介紹
cp 指令主要用於複製檔案或者目錄。
典型用法:
cp [options] source dest
詳細的選項引數說明可以查閱相關資料。此處只介紹 cp 指令複製檔案的功能。
如果目標檔案 dest 不存在,則 cp 會建立這個檔案。如果存在,則用 source 檔案的內容覆蓋 dest 檔案。
cp如何複製檔案
實現 cp 指令的複製檔案功能有兩個關鍵點:首先,需要能夠建立一個新檔案或者清空已有檔案。其次,能夠把原檔案的內容寫入到目標檔案中。
- 建立檔案
Linux 有一個系統函式 creat() 既能新建立一個檔案,也能清空已有檔案。其函式原型如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char *pathname, mode_t mode);
引數 pathname,為檔名
引數 mode,檔案訪問模式。模式定義如下,
S_IRWXU 00700 檔案所有者擁有讀、寫、執行許可權 S_IRUSR 00400 檔案所有者擁有讀許可權 S_IWUSR 00200 檔案所有者擁有寫許可權 S_IXUSR 00100 檔案所有者擁有執行許可權 S_IRWXG 00070 同組使用者擁有讀、寫、執行許可權 S_IRGRP 00040 同組使用者擁有讀許可權 S_IWGRP 00020 同組使用者擁有寫許可權 S_IXGRP 00010 同組使用者擁有執行許可權 S_IRWXO 00007 其他使用者擁有讀、寫、執行許可權 S_IROTH 00004 其他使用者擁有讀許可權 S_IWOTH 00002 其他使用者擁有寫許可權 S_IXOTH 00001 其他使用者擁有執行許可權
此函式等效於,利用 open 函式實現建立新檔案或者清空已有檔案,並開啟此檔案:
open(const char * pathname, (O_CREAT | O_WRONLY | O_TRUNC), mode_t mode);
creat() 函式告訴核心建立一個名字為 pathname 的檔案,如果不存在,就建立它;如果存在就把它的內容清空,檔案長度設定為 0。
建立成功,會返回一個指向此檔案的檔案描述符。建立失敗,則返回 -1 。
檔案建立成功後,檔案的訪問許可位,被設定為 mode 引數的值。
- 寫檔案
寫檔案系統呼叫函式為 write(),函式原型如下
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
引數 fd,為檔案描述符。
引數 buf,儲存寫入資料的記憶體指標(地址)。
引數 count,為要寫入資料的位元組個數。
這個系統呼叫函式告訴核心將記憶體中指定的資料寫入檔案。如果核心不能寫入或者寫入失敗,wirte 返回 -1。如果寫入成功,則返回實際寫入的位元組數。
有時候會出現,寫入的位元組數少於要求的數目。原因有二:(1)有的系統對檔案的最大尺寸有限制;(2)磁碟空間接近滿了。
核心會盡量把資料寫入檔案,並將實際寫入的位元組數返回。
編寫 cp 程式碼
程式的執行流程:
編寫程式程式碼 cp1.c :
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
/* 定義緩衝區大小的巨集 */
#define BUFFSIZE 4096
/* 檔案訪問模式,檔案所有者擁有讀寫許可權,同組使用者擁有讀許可權,其他使用者擁有讀許可權 */
#define COPYMODE 0644
/* 錯誤資訊顯示函式 */
void oops(char *s1, char *s2);
int main(int ac, char *av[])
{
int in_fd, out_fd, n_chars;
char buf[BUFFSIZE];
/* 判斷引數個數 */
if(ac != 3)
{
fprintf(stderr, "usage: %s source destination\n", *av);
exit(1);
}
/* 開啟原檔案 */
if((in_fd = open(av[1], O_RDONLY)) == -1)
{
oops("Cannot open ", av[1]);
}
/* 建立目標檔案 */
if((out_fd = creat(av[2], COPYMODE)) == -1)
{
oops("Cannot creat ", av[2]);
}
/* 讀原檔案內容,並判斷讀取的位元組數是否大於 0 */
while((n_chars = read(in_fd, buf, BUFFSIZE)) > 0)
{
/* 將讀取的資料寫入檔案 */
if(write(out_fd, buf, n_chars) != n_chars)
{
oops("Write error to ", av[2]);
}
}
/* 如果讀檔案出錯,給出錯誤資訊 */
if(n_chars == -1)
{
oops("Read error from ", av[1]);
}
/* 關閉原檔案和目標檔案 */
if((close(in_fd) == -1) || (close(out_fd) == -1))
{
oops("Error closing files", "");
}
}
/* 錯誤資訊輸出 */
void oops(char *s1, char *s2)
{
fprintf(stderr, "Error: %s", s1);
perror(s2);
exit(1);
}
編譯,並測試以上程式碼:
$ gcc cp1.c -o cp1
$ ./cp1 test test_cp1
$ ls test test_cp1 -l
-rwxrwxr-x 1 user user 8728 11月 3 00:10 test
-rw-r--r-- 1 user user 8728 11月 3 00:12 test_cp1
可以看到我們編寫的程式能正常執行,可以完成檔案的複製工作。原檔案和目標檔案的大小和內容相同。
看看程式對錯誤輸入的反應:
$ ./cp1 tes test_cp1
Error: Cannot open tes: No such file or directory
$ ./cp1 test
usage: ./cp1 source destination
$ ./cp1 test /tmp
Error: Cannot creat /tmp: Is a directory
總結
通過分析 cp 指令的原理,完成 cp 複製檔案的功能程式碼,學習了 Linux 程式設計實現對檔案的讀寫操作。
涉及到的系統函式:
- open 開啟一個檔案
- creat 建立一個檔案
- read 讀取檔案內容
- write 向檔案寫入資料
- close 關閉開啟的檔案
關注公眾號【一起學嵌入式】,獲取更多技術乾貨。