一、如何了解檔案IO(了解)
在Linux下有兩種檔案操作方法 标準IO 檔案IO
标準IO與檔案IO的差別:
标準IO : 遵循C标準 、帶緩沖 、 流(FILE結構體)
檔案IO: POSIX規範 、無緩沖 、 檔案描述符(fcl)
什麼是檔案IO:
— posix(可移植作業系統接口) 定義的一組函數。
— 不提供緩沖機制,每次讀寫操作都引起系統調用。
— 核心概念是 檔案描述符。
— 通路各種類型檔案。(除了普通檔案,終端檔案,Linux下7中檔案類型都可以通路)
— Linux下,标準IO基于 檔案IO。(基于檔案IO 封裝了一個 緩沖機制)
二、檔案IO描述符的含義(了解)
- 每個打開的檔案都對應一個檔案描述符。
- 檔案描述符是一個非負整數。Linux為程式中每打開的檔案配置設定一個檔案描述符。
- 檔案描述符從0開始配置設定,依次遞增,且每次配置設定都是先找最小非負整數配置設定。
例如:打開了0,1,2三個檔案,關閉1後再打開一個檔案,配置設定的檔案描述符為1.
- 不同函數中 相同檔案描述符 代表的是不同的檔案,互不影響。
- 檔案IO操作通過檔案描述符來完成。
- 0,1,2的含義?
每打開一個檔案,系統自動建立三個檔案(标準輸入stdin(0),标準輸出stdout(1),标準錯誤stderr(2))使用這三個檔案描述符表示。
三、檔案的打開和關閉(了解)
1、檔案IO ------ open
- Open :函數用來建立或打開一個檔案。
#include <fcntl.h>
Int open(const char *path,into flag,……..);
Path: 要打開檔案的路徑。
Flag: 要打開的模式。
- 成功時傳回檔案描述符:出錯時傳回EOF
- 打開檔案時使用兩個參數。
- 建立檔案時第三個參數指定新檔案的權限。
- 隻能打開裝置檔案,不能用來建立裝置檔案,裝置檔案需要使用專門的函數建立。

示例:
以隻寫的形式打開檔案test.txt ,如果檔案不存在則建立,如果檔案存在則清空。
以隻寫的形式打開檔案test.txt ,如果檔案不存在則建立,如果檔案存在則報錯。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int fp;
if(argc < 2)
{
printf("Usage : %s <src_file> <dst_file> \n",argv[0]);
return -1;
}
if((fp = open(argv[1],O_WRONLY|O_CREAT|O_EXCL,0666))<0)
{
printf("%s:%s \n",argv[1],strerror(errno));
//error("argv[1])");
return -1;
}
close(fp);
return 0;
}
2、檔案IO ------ close
#include <unistd>
Int close(int fd)
- 成功傳回0 ;失敗傳回EOF
- 程式結束時會自動關閉打開的檔案。
- 檔案關閉後,其對應的檔案描述符将與該檔案無關。
- 系統打開的檔案個數有限(Linux 1024個),是以打開的檔案使用完後應該及時關閉。
四、檔案的讀取、寫入及定位(了解)
1、讀取檔案(熟練)
#include <unistd>
ssize_t read(int fd , int *buf ,int count);
- Fd : 檔案描述符 buf : 存放讀出資料的緩沖區 count : 需要讀出的位元組數
- 成功時 傳回實際讀出的位元組大小, 失敗傳回 EOF ; 檔案的資料少于指定讀出的大小,有多少讀出多少并傳回實際讀出大小;讀到檔案末尾 傳回0;
- Buf 是接收資料的緩沖區。
- Conunt的大小不應該 大于 buff的大小。
執行個體: 從指定的檔案(文本檔案)中,讀取檔案并統計大小。
2、寫入檔案(熟練)
#include <unistd>
ssize_t write(int fd , int *buf ,int count);
- Fd : 檔案描述符 buf : 存放寫入資料的緩沖區 count : 需要寫入的位元組數
- 成功時 傳回實際寫入的位元組大小, 失敗傳回 EOF ;
- Buf 是發送資料的緩沖區。
- Conunt的大小不應該 大于 buff的大小。
3、定位檔案(熟練)
Lseek() 函數用來定位檔案
#include <unistd.h>
off_t lseek (int fd,off_t offset, int whence);
- 成功時傳回目前的檔案讀寫的位置;出錯時傳回EOF。
- 參數offset和參數wehence 同fseek 完全一樣。
— 思考與練習
- 利用檔案IO實作檔案的複制。
檔案名通過指令行參數的拟定
打開檔案的方式?
如何判斷是否讀到了源檔案的末尾?
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
int fp;
int fpd;
int buff[20];
int ch;
if(argc<3) //判斷一下輸入的參數 是否小于三個
{
printf("Usage : %s <src_file><dst_file>\n",argv[0]);
return -1;
}
if((fp = open(argv[1],O_RDONLY)) == -1) // 以隻讀 方式打開必須存在的檔案
{
perror("fopen src file ");
return -1 ;
}
if((fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666)) == -1) //以隻寫 清空方式打開檔案,不存在則 建立
{
perror("fopen dst file ");
return -1 ;
}
while((ch = read(fp,buff,20))>0) // 從檔案中讀出資料放入 buffhu緩沖中
{
fwrite(fpd,buff,20); // 将buff緩沖中的 資料 寫入 檔案fpd中
}
fclose(fp); //關閉檔案 fp
fclose(fpd); //關閉檔案 fp
return 0;
}
五、檔案目錄的操作
1、讀取目錄
opendir 函數用打開一個目錄檔案。
#include <dirent.h>
DIR *opendir(const char * name);
- DIR是用來描述一個打開的目錄檔案的結構體類型。
- 成功是傳回目錄的流指針,出錯時傳回NULL.
readdir 函數用來讀取目錄流中的内容。
struct dirent * readdir(DIR * dirp);
- struct dirent 是用來描述目錄流中一個目錄項的結構體類型。
- 包含成員 char d_name[256] 等 (具體參考幫助文檔)
- 成功時傳回目錄流dirp中下一個目錄項。
- 出錯或到末尾時 傳回NULL。
On Linux, the dirent structure is defined as follows:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
示例:列印指定的目錄下所有檔案名稱。
Int main(argc,char *argv[])
{
DIR *dirp;
struct dirent *dp;
if(argc <2 )
{
printf(“Usage:%s <directory> \n”,argv[0]);
return -1;
}
if((dirp = opendir(argv[1] ))==NULL)
{
perror(“opendir”);
return -1;
}
while(dp = readdir(dirp) !=NULL)
{
printf(“%s \n”,dp->d_name);
}
closedir(dirp);
}
注意: 如果該目錄中 還有 子目錄檔案,該函數是讀取不出來的,該函數隻能讀取目前目錄下的是以有内容。
2、修改檔案通路權限
Chmod / fchmod 函數用來修改檔案的通路權限
#include <sys/stat.h>
Int chmod (const char *path,mode_t mode);
Int fchmod(int fp, mode_t mode );
- 成功時傳回0,出錯時傳回EOF.
- Root 和檔案的擁有者能修改檔案的通路權限(其他人不能修改)
示例:
Chmod (“test.txt”,0666); //以八進制的形式
3、擷取檔案屬性
stat / lstat/fstat 函數 用來擷取檔案屬性。
int stat (const char* path, struct stat *buf);
int lstat(const char* path, struct stat *buf);
int fstat( int pd, struct stat *buf);
- 成功時傳回0;失敗時傳回EOF。
- 如果path是符号連結符,stat函數擷取的是 目标檔案的屬性,而lstat 擷取的是連結檔案的屬性。 (一般使用 lstat函數)
- struct stat 結構體定義如下:
struct stat {
dev_t st_dev; // 檔案所在裝置ID
ino_t st_ino; // 結點(inode)編号
mode_t st_mode; // 保護模式
nlink_t st_nlink; // 硬連結個數
uid_t st_uid; // 所有者使用者ID
gid_t st_gid; // 所有者組ID
dev_t st_rdev; // 裝置ID(如果是特殊檔案)
off_t st_size; // 總體尺寸,以位元組為機關
blksize_t st_blksize; // 檔案系統 I/O 塊大小
blkcnt_t st_blocks; // 已配置設定 512B 塊個數
time_t st_atime; // 上次通路時間
time_t st_mtime; // 上次更新時間
time_t st_ctime; // 上次狀态更改時間
};
Struct stat 是存放檔案屬性的結構體類型:
- Mode_t st_mode ; 類型和通路權限
- Uid_t st_uid; 所有者id
- Uid_t st_gid; 使用者組id
- Off_t st_size; 檔案大小
- Time_t st_mtime; 最好修改時間
通過系統提供的宏來判斷檔案類型:
St_mode & 0170000
- S_ISREG(st_mode) 0100000
- S_ISDIR(st_mode) 0040000
- S_ISCHR(st_mode) 0020000
- S_ISBLK(st_mode) 0060000
- S_ISFIFO(st_mode) 0010000
- S_ISLNK(st_mode) 0120000
- S_ISSOCK(st_mode) 0140000
S_IFMT 0170000 檔案類型位域掩碼
S_IFSOCK 0140000 套接口
S_IFLNK 0120000 符号連結
S_IFREG 0100000 普通檔案
S_IFBLK 0060000 塊裝置
S_IFDIR 0040000 目錄
S_IFCHR 0020000 字元裝置
S_IFIFO 0010000 FIFO
S_ISUID 0004000 設定 UID 位
S_ISGID 0002000 設定 組ID 位 (看下面)
S_ISVTX 0001000 粘滞位(看下面)
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 其他有執行權限
通過系統提供的宏來擷取檔案通路權限:
- S_IRUSR 00400 bit:8
- S_IWUSR 00200 7
- S_IXUSR 00100 6
- S_IRGRP 00040 5
- S_IWGRP 00020 4
- S_IXGRP 00010 3
- S_IROTH 00004 2
- S_IWOTH 00002 1
- S_IXOTH 00001 0
示例:擷取并顯示檔案屬性
以下面格式列印指定檔案的主要資訊:
$ ./a.out test.c
-rw-r—r-- 317 2019 – 11 – 18 test.c
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, const char *argv[])
{
struct stat buf;
struct tm *tp;
int n;
if(argc<2)
{
printf("Usage : %s <file>\n",argv[0]);
return -1;
}
if(lstat(argv[1],&buf) < 0)
{
perror("lstat");
return -1;
}
switch(buf.st_mode & S_IFMT) //辨識 檔案類型
{
case S_IFREG:
printf("-");
case S_IFDIR:
printf("d");
break;
}
for(n=8;n>=0;n--)
{
if(buf.st_mode & (1<<n)) // 辨識 檔案的 權限 (9位)
{
switch(n%3)
{
case 2:
printf("r");
break;
case 1:
printf("w");
break;
case 0:
printf("x");
break;
}
}
else
{
printf("-");
}
}
printf(" %lu",buf.st_size); //輸出檔案的目錄的大小
tp = localtime(&buf.st_mtime); //得到上次更新時間
printf(" %d-%02d-%02d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday);
printf(" %s\n",argv[1]);
return 0;
}