Linux下的文件IO編程

        Linux 系統調用(system call)是指操作系統提供給用戶程序的一組“特殊接口”,用戶程序可以通過這組“特殊”接口來獲得操作系統提供的特殊服務。
        爲了更好的保護內核空間,將程序的運行空間分爲內核空間和用戶空間,他們運行在不同的級別上,在邏輯上是相互隔離的。在 Linux 中,用戶程序不能直接訪問內核提供的服務,必須通過系統調用來使用內核提供的服務。
        Linux 中的用戶編程接口(API)遵循了 UNIX 中最流行的應用編程界面標準—— POSIX。這些系統調用編程接口主要是通過 C 庫(libc)實現的。
        往期傳送門
        史上最全的Linux常用命令彙總(超詳細!超全面)收藏這一篇就夠了
        Linux下標準IO的這些操作你清楚嗎(內附有詳細的介紹和例程)

文件I/O簡介

  • POSIX(可移植操作系統接口)定義的一組函數
  • 不提供緩衝機制,每次讀寫操作都執行系統調用
  • 核心概念是文件描述符
  • 訪問各種類型文件

文件描述符

  • 每個打開文件對應一個文件描述符
  • 文件描述符是一個非負整數,Linux爲程序中每個打開的文件分配一個文件描述符
  • 文件描述符從0開始分配,依次遞增
  • 文件IO操作通過文件描述符完成

注意每個程序中打開的文件系統會單獨分配文件描述符,互相不影響

文件I/O與標準I/O區別

標準I/O 文件I/O
ANSIC POSIX
帶緩衝(減少系統調用次數) 無緩衝(讀寫文件需要進行系統調用
流(FILE結構體)打開文件 文件描述符表示一個打開的文件

打開文件(open)

open函數用來創建或者打開一個文件:

#include <fcntl.h>
int open(const char *path,int oflag,...);
//參數1:打開文件路徑 參數2:打開方式
  • 成功時返回文件描述符;出錯時返回EOF
  • 打開文件時使用兩個參數
  • 創建文件時第三個參數所指定新文件的權限
  • 只能打開設備文件

Alt

示例1:
        以只寫的方式打開1.txt。如果文件不存在就創建,如果文件存在就清空。
int fd;
if((fd=open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666))<0
{
	perror("open");
	return -1;
}
示例2:
        以讀寫方式打開文件1.txt。如果文件不存在則創建,如果文件存在則報錯:
int fd;
if((fd=open("1.txt",O_RDWR|O_CREAT|O_EXCL,0666))<0)
{
	if(errno==EEXIST)
	{
		perror("exist error");
	}
	else
	{
		perror("other error");
	}
}

關閉文件(close)

close函數用來關閉一個打開的文件:

#include <unistd.h>
int close(int fd);
  • 成功時返回0;出錯時返回EOF
  • 程序結束時自動關閉所有打開的文件
  • 文件關閉以後不能再對文件進行任何操作

讀取文件(read)

       read函數用來從文件中讀取數據:

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);
  • 成功時返回實際讀取的字節數;出錯時返回EOF
  • 讀到文件末尾時返回0
  • buf是接收數據的緩衝區
示例
       從指定的文件(文本文件)中讀取內容並統計大小
#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd,n,total=0;
	char buff[64];
	if(argc<2){
		prinff("Usage:%s<file>\n",argv[0];
		return -1;
	}
	if((fd=open(argv[1],O_RDONLY))<0){
		perror("open";
		return -1;
	}
	while((n=read(fd,buf,64))>0){//讀取64個字節到buf中並保存返回值到n
		total+=n;//total的值即爲文件的大小
	}
	printf("size:%d\n",total);
	return 0;
}

寫入文件(write)

       write函數用來向文件寫入數據:

#include <unistd.h>
ssize_t write(int fd,void *buf,size_t count);//buf寫入內容;count寫入大小
  • 成功時返回實際寫入的字節數;出錯時返回EOF
  • buf是發送數據的緩衝區
  • count不應超過buf的大小
示例
       將鍵盤輸入的內容寫入文件,直到輸入quit結束:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
	int fd;
	char buf[20];
	//只寫方式打開文件,如果文件不存在創建,如果文件存在則清空
	if((fd =open(argv[1],O_WRONLY|OCREAT|O_TRUNC,0666))<0{
		perror("open");
		return -1;
	}
	while(fgets(buf,20,stdin)!=NULL){
		if(strcmp(buf,"quit\n")==0)break;
		write(fd,buf,strlen(buf));
	}

定位文件(lseek)

       lseek函數用來定位文件:

#include <unistd.h>
off_t lseek(int fd,off_t offset,intt whence);
  • 成功時返回當前文件的讀寫位置;出錯時返回EOF
  • 參數offset和參數whence同fseek完全一樣

訪問目錄(opendir/readdir)

       opendir函數用來打開一個目錄文件

#include <dirent.h>
DIR *opendir(const char *name);
  • DIR是用來描述一個打開的目錄文件的結構體類型
  • 成功時返回目錄流指針;出錯時返回NULL

       readdir函數用來讀取目錄流中的內容

#include <dirent.h>
struct sirent *readdir(DIR *dirp);
  • struct dirent是用來描述目錄流中一個目錄項的結構體類型
  • 包含char d_name[256]等成員
  • 成功時返回目錄流dirp中下一個目錄項
  • 出錯或到末尾時返回NULL

關閉目錄(closedir)

       closedir用來關閉一個目錄文件:

#include <dirent.h>
int closedir(DIR *drip);
示例:
       打印指定目錄下所有文件的名稱:
#include <stdio.h>
#include <dirent.h>
int main(int argc,char *argv[])
{
	DIR *drip;
	struct dirent *dp;
	if(argc<2){
		printf("Usage :%s<directory>\n"argc[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);
	return 0;
}

修改文件屬性(chmod/fchmod)

       chmod/fchmod函數用來修改文件的訪問權限:

#include <sys/stat.h>
int chmod(const char *path,mode_t mode);
int fchmod(int fd,mode_t mode);
  • 成功時返回0;出錯時返回EOF
  • root和文件所有者能修改文件的訪問權限

獲取文件屬性(stat/lstat/fstat)

#include <stdio.h>
int stat(const char *path,struct stat *buf);
int lstat(const char *path,struct stat *buf);
int fstat(int fd,struct stat *buf);
  • 成功時返回0;出錯時返回EOF;
  • 如果path是符號鏈接stat獲取的是目標文件的屬性;而lstat獲取的是鏈接文件的屬性

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通過系統提供的宏來判斷文件類型:
通過(st_mode&0170000)計算後得到的值和以下進行匹配

文件類型 計算值
S_ISREG(st_mode) 普通文件 0100000
S_ISDIR(st_mode)目錄文件 0040000
S_ISCHR(st_mode) 0020000
S_ISBLK(st_mode) 0060000
示例
       獲取並顯示文件屬性:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc,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_IFRGE:
			printf("-");
			break;
		case S_IFDIR:
			printf("d");//是一個目錄文件
			break;
	}
	for(n=8;n>=0;n--)
	{
		if(buf.st_mode&(1<<n))
		{
			switch(n%3)
			{
			case 2:
				printf("r");
			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",argc[1]);
	return 0;
}

        不積小流無以成江河,不積跬步無以至千里。而我想要成爲萬里羊,就必須堅持學習來獲取更多知識,用知識來改變命運,用博客見證成長,用行動證明我在努力。
       如果我的博客對你有幫助、如果你喜歡我的博客內容,記得“點贊” “評論” “收藏”一鍵三連哦!聽說點讚的人運氣不會太差,每一天都會元氣滿滿呦!如果實在要白嫖的話,那祝你開心每一天,歡迎常來我博客看看。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章