linux系統編程--文件IO

文件操作

inux中一切皆文件!
只要掌握了文件操作,即可對linux下所有資源進行操作。
文件操作就是對文件進行 讀 寫 操作。

對文件進行操作時,文件名是不能明確指定一個文件空間的,需要根據文件名首先找到對應inode號。
一個inode號對應着一個固定的文件空間,對文件操作時,使用inode號進行操作,才能保證操作無誤!

對一個文件基本的操作,只有:讀、寫
讀:把文件內容讀取出來,查看文件內容
寫:編輯文件,修改文件內容。
在對文件進行讀寫操作之前,可以使用文件偏移指針指定要操作文件的哪一塊空間。
總結:對文件的操作方式有:讀、寫、偏移!!!

當想要對一個文件進行操作的時候,首先指定要操作文件的文件名。因爲文件名不能準確的表示一塊文件空間,所以在指定好要操作的文件名之後,通過調用open()函數,打開文件。該操作可以根據提供的文件名得到對應的inode號,然後對inode號進行封裝,得到操作該文件的文件結構體,文件結構體在應用程序中表現爲文件描述符。
接下來,利用open()之後得到的文件描述符,系統就可以找到要操作的文件空間,然後通過調用文件操作偏移指針定位函數lseek(),指定要操作文件中的哪一塊空間,指定好之後,就可以對文件進行讀寫操作了,讀寫操作調用read()/write()函數。當對文件操作完成之後,關閉文件即可。

對文件操作中,所有的信息全部都存放在文件結構體中,文件描述符就是指向文件結構體的指針。

文件描述符

1)文件描述符是操作一個文件的節點。
2)應用程序中每打開一個文件,就會得到文件的文件結構體,結構體指針存放在應用程序中自動定義的文件描述符數組中,按照數組的下標從小到大依次存放。
3)文件描述符本質上是一個數組的下標。
4)文件描述符的範圍是:0 ~ 1023
5)當應用程序運行起來,文件描述符數組空間分配,應用程序結束,文件描述符空間釋放。
6)文件描述符中存放有對文件的操作方式。
7)文件描述符數組中前面三個元素在應用程序運行時立即自動佔用。
0:標準輸入 鍵盤 1:標準輸出 屏幕 2:標準異常 屏幕
8)進程打開的第2+x個文件,文件描述符不是ionde號

文件操作相關函數

對一個文件進行操作,第一步必須先打開文件,得到文件對應的文件描述符。
打開文件:open();
函數原型:int open(const char *pathname, int flags, mode_t mode);
函數功能:根據提供的帶路徑的文件名(pathname),得到對應的文件描述符,通過返回值返回。
形參列表:
pathname:帶路徑的文件 --> 指定要操作的文件
flags:對當前文件的操作方式
三個務必選一個!!!
O_RDONLY :只讀
O_WRONLY :只寫
O_RDWR :讀寫

可選!
O_CREAT :創建文件(如果文件不存在,則創建。如果文件存在,則跳過創建,直接打開)
O_EXCL :檢測文件是否存在(和O_CREAT結合使用,如果文件不存在,則創建,如果文件存在,出錯)
O_TRUNC :清空寫
O_APPEND :追加寫

O_WRONLY :只寫
O_TRUNC :清空寫
O_APPEND :追加寫

O_RDWR :讀寫
O_TRUNC :清空寫 trunc
O_APPEND :追加寫

O_CREAT creat

代碼寫的權限經過掩碼操作後的纔是最後的權限。
 umask 默認爲0002(八進制) 000 000 010 (二進制)
0777 & ~(umask)
111 111 111
111 111 101
mode:文件權限 (文件對不同用戶的權限)
  0764
當創建新文件時,該參數必須。
當打開已存在的文件時,該參數不需要。
返回值:
  成功:指定的文件對應的文件描述符 – fd
  失敗:-1
註釋:文件權限和權限掩碼進行運算,得到最終的文件權限
  權限掩碼:umask

思考01.文件如果不存在,是否創建
  創建: O_CREAT
   是否需要檢測?

思考02.如果文件存在,我要對文件進行什麼操作?
  只讀:O_RDONLY
  只寫:O_WDONLY
  讀寫:O_RDWD
思考03.如果是寫操作,怎麼寫
  覆蓋:
  追加:O_APPEND

小練習:
   調用open函數,編程實現touch命令的功能。

分析touch
   touch新建一個文件
例如: touch abc.c
  argc=2
  argv:”touch”,abc.c

  當打開一個文件,得到文件描述符之後,就可以對文件進行讀寫操作了。

  #include<stdio.h>
  #include<sys/types.h>
  #include<sys/stat.h>                                                                       
  #include<fcntl.h>
  #include<unistd.h>
  int main (int argc,char *argv[])
   {
       if (argc !=2)
      {
          printf ("請輸入想要創建的文件名\n");
      }
     int fd=open(argv[1],O_RDONLY|O_CREAT,0666);
      close(fd);
     return 0;
 }

測試:

[root@local 01_file_io]# ls
my_touch.c
[root@local 01_file_io]# gcc my_touch.c -o my_touch.exe
[root@local 01_file_io]# ls
my_touch.c  my_touch.exe
[root@local 01_file_io]# ./my_touch.exe 
請輸入想要創建的文件名
[root@local 01_file_io]# ./my_touch.exe test.txt
[root@local 01_file_io]# ls
my_touch.c  my_touch.exe  test.txt

read();

函數原型:ssize_t read(int fd, void *buf, size_t count);
函數功能:從fd指向的文件中讀取最多count個字節的數據,存放在buf指向的緩存區
  參數分別是:從哪讀,讀到哪 ,讀多少,
形參列表:
  fd:文件描述符 --> 指向要操作的文件
  buf:緩存區首地址 --> 用來存放從文件中讀取出來的數據
  count:從文件中讀取數據的上限
返回值:
  成功:實際讀取的字節數
  失敗:-1

實現cat命令

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
        int fd = 0;
        fd = open(argv[1], O_RDONLY);

        if(fd < 0)
        {
                printf("open %s failed!\n", argv[1]);
                return -1;
        }

        int ret = 0;
        char buf[32] = {0};
        ret = read(fd, buf, sizeof(buf));
        printf("ret: %d\n", ret);
        printf("buf: %s\n", buf);
        return 0;
}

執行結果:

my_cat.c  my_cat.exe  text.txt
[root@local read]# ./my_cat.exe text.txt 
ret: 4
buf: 123

write();

函數原型:ssize_t write(int fd, const void *buf, size_t count);
函數功能:從buf指向的緩存區中讀取數據,向fd指向的文件中寫。最大寫入count個字節的數據。
形參列表:
  fd:文件描述符 --> 指向要操作的文件
  buf:緩存區首地址 --> 指向存放要寫入文件的數據的空間
  count:最大寫入文件的字節數 (實際寫入的字節數)
  寫到哪個文件(文件描述符)
  寫什麼內容(buf)
  寫多少個(sizeof)
返回值:
  成功:實際寫入文件的數據字節數
  失敗:-1
  註釋:寫的方式有三種 --> 覆蓋寫、清空寫、追加寫

注意:
1)count傳參的值就是實際寫入文件的字節個數,如果可見字符不足,用0補充!
2)打開文件時,有寫(只寫/讀寫)操作方式,寫的方式就是覆蓋寫。
3)打開文件時,有O_TRUNC操作方式,寫的方式就是清空寫。
4)打開文件時,有O_APPEND操作方式,寫的方式就是追加寫。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
int main(int argc, char* argv[])
{
	int fd = 0;
	fd = open(argv[1], O_WRONLY | O_CREAT | O_APPEND, 0666);	
	char buf[32] = "how are you 你是誰";
	write(fd, buf, strlen(buf));
	return 0;
}

Close();

int close(int fd);

成功返回0;失敗返回-1;

文件操作:
文件描述符!
open(); read(); write(); lseek(); close

文件描述符:
1)文件描述符是操作一個文件的句柄!
2)文件描述符是應用程序中自動創建的文件描述符數組的數組下標
3)文件描述符的0/1/2已經被佔用。
4)在應用程序中打開一個文件,得到的文件描述符是從3開始,從小到大連續分配的整型數據。
5)應用程序運行,文件描述符數組分配,應用程序結束,文件描述符數組釋放。
6)文件描述符中存放有文件的操作方式。
7)在執行讀寫操作時,文件偏移指針會自動往後偏移。
8)文件描述符中存放着文件操作的偏移指針。
9)在一個應用程序打開同一個文件多次,可以得到多個相互獨立的文件描述符。
每一個文件描述符中都有自己的操作方式及文件偏移指針。
10)文件描述符的取值範圍爲:0 ~ 1023。
11)文件描述符是一種個數有限的,可重複利用的資源!
12)每次打開文件都會得到最小的、空閒的文件描述符。

三種寫操作,寫的特性在open一個文件的時候生效!
以追加寫方式進行操作, 寫操作時從文件末尾開始寫,如果執行讀操作,則從文件開頭開始讀!

文件偏移指針的特性:
1)在讀寫操作時,文件偏移指針會自動偏移
2)文件偏移指針指定文件操作的位置
3)讀寫操作共用文件描述符中的同一個文件偏移指針
4)同一個文件可以打開多次,得到不同的文件描述符,每一個文件描述符中都有一個獨立的偏移指針。

問題1:文件偏移指針存放在哪?
每一個文件描述符都有一個獨立的文件偏移指針! (一個文件打開兩次,得到兩個文件描述符,分別對文件操作)
問題2:對文件進行讀寫操作時,文件偏移指針是否是同一個?
使用同一個文件描述符對文件進行讀寫操作時,文件偏移指針共用的!

  1. 文件偏移指針定位
    文件偏移指針指定下次文件讀寫操作的起始位置!
    有時候,需要自己定位文件偏移指針的位置,選擇下一次操作的文件內容。
    文件偏移指針定位函數: lseek();

lseek();

函數原型:off_t lseek(int fd, off_t offset, int whence);
函數功能:對文件偏移指針進行重新定位
形參列表:
  fd:文件描述符 – 要操作的文件
  offset:相對於基準點的偏移距離 (負數:向前偏移 正數:向後偏移)
whence:文件偏移指針定位的基準點
SEEK_SET :文件開頭
SEEK_CUR:文件偏移指針當前位置
SEEK_END:文件結尾
返回值:
  成功:文件偏移指針當前位置到文件開頭的距離
  失敗:-1
  註釋:可以把文件偏移指針定位到文件結尾,這時lseek函數的返回值就是文件大小!

小練習

使用read/write函數,實現文件的加密/解密。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void  jm(char* p, int count);
void dejm(char* p, int count);
int main(int argc, char* argv[])
{
	int fd = 0;
	int fp = 0;
	fd = open(argv[1], O_RDONLY);
	fp = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);
	if((fd < 0) || (fp < 0))
	{
		printf("open failed!\n");
		return -1;
	}

	int ret = 0;
	char buf[32] = {0};
	do
	{
		memset(buf, 0, sizeof(buf));
		ret = read(fd, buf, sizeof(buf));
		if(strcmp(argv[3], "-j") == 0)
		{
			printf("---加密---\n");
			jm(buf, ret);
		}
		else if(strcmp(argv[3], "-d") == 0)
		{
			printf("---解密---\n");
			dejm(buf, ret);
		}
		else
		{
			printf("---複製---\n");
		}
		write(fp, buf, ret);
	}
while(ret > 0);
	return 0;
}

void  jm(char* p, int count)
{
	int i = 0;
	for(i = 0; i < count; i++)
	{
		p[i] = p[i] + i*(i+2);   //加密算法
	}
}

void dejm(char* p, int count)
{
	int i = 0;
	for(i = 0; i < count; i++)
	{
		p[i] = p[i] - i*(i+2);
	}
}

翻倍
1)靈活使用lseek函數,實現文件的內容翻倍。
只能用一個文件描述符,只能用一個文件。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
	int fd = 0;
	fd = open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("open failed!\n");
		return -1;
	}// 1. 得到文件大小
	int length = 0;
	length = lseek(fd, 0, SEEK_END);//文件總長度
	lseek(fd, 0, SEEK_SET);
	int roff = 0;
	int ret = 0;
	char buf[32] = {0};
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		lseek(fd, roff, SEEK_SET);
		ret = read(fd, buf, sizeof(buf));//每次讀32個字節
		roff +=  ret; 	
	if(roff >= length)
		{
			ret = roff - length;		//超出

			ret = sizeof(buf) - ret;	
		    lseek(fd, 0, SEEK_END);
		        write(fd, buf, ret);
			break;
		}
		lseek(fd, 0, SEEK_END);
		write(fd, buf, ret);
	}
	return 0;
}

通過調用read函數,實現cat命令的功能。(能夠讀取任意大小的文件)

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
	int fd = 0;
	fd = open(argv[1], O_RDONLY);//argv[0]爲函數本身
	if(fd < 0)
	{
		printf("open %s failed!\n", argv[1]);
		return -1;
	}
	int ret = 0;
	char buf[32] = {0};
	do
	{
		memset(buf, 0, sizeof(buf));    //重複讀,函數指針會自動偏移
		ret = read(fd, buf, sizeof(buf)-1);
		printf("%s", buf);
	}while(ret > 0);//不夠嚴謹

//while(1)
//{
// ret =read(fd,buf,128);
//if(ret<128)//讀不夠128個字節了,結束
//   {
//    printf(“%s,buf”);
//    break;
//   }
//   printf("%s", buf);
//}

	return 0;
}

通過調用read/write函數,實現cp命令的功能
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
	int fd = 0;
	int fp = 0;
	fd = open(argv[1], O_RDONLY);
	fp = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);//需要創建的時候就要寫權限
	if((fd < 0) || (fp < 0))
	{
		printf("open failed!\n");
		return -1;
	}

	int ret = 0;
	char buf[32] = {0};
	do
	{
		memset(buf, 0, sizeof(buf));
		ret = read(fd, buf, sizeof(buf));
		write(fp, buf, ret);
	}while(ret > 0);
	return 0;
}

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