文件操作
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:對文件進行讀寫操作時,文件偏移指針是否是同一個?
使用同一個文件描述符對文件進行讀寫操作時,文件偏移指針共用的!
- 文件偏移指針定位
文件偏移指針指定下次文件讀寫操作的起始位置!
有時候,需要自己定位文件偏移指針的位置,選擇下一次操作的文件內容。
文件偏移指針定位函數: 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;
}