關於write和read以及檔案讀寫位置
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbyte);
fd:檔案描述符
buf:指定的緩衝區,即指標,指向一段記憶體單元
nbyte:要寫入檔案指定的位元組數
返回值:寫入文件的位元組數(成功);-1(出錯)
write函式把buf中nbyte寫入檔案描述符handle所指的文件,成功時返回寫的位元組數,錯誤時返回-1.
read
ssize_t read (int fd, void *buf, size_t count);
返回值
成功返回讀取的位元組數,出錯返回-1並設定errno
如果在調read之前已到達檔案末尾,則這次read返回0
引數
引數count是請求讀取的位元組數,讀上來的資料儲存在緩衝區buf中,同時檔案的當前讀寫位置向後移。
注意這個讀寫位置和使用C標準I/O庫時的讀寫位置有可能不同,這個讀寫位置是記在核心中的,而使用C標準I/O庫時的讀寫位置是使用者空間I/O緩衝區中的位置。
比如用fgetc讀一個位元組,fgetc有可能從核心中預讀1024個位元組到I/O緩衝區中,再返回第一個位元組,這時該檔案在核心中記錄的讀寫位置是1024,而在FILE結構體中記錄的讀寫位置是1。
注意返回值型別是ssize_t,表示有符號的size_t,
這樣既可以返回正的位元組數、0(表示到達檔案末尾)
也可以返回負值-1(表示出錯)
read函式返回時,返回值說明了buf中前多少個位元組是剛讀上來的。
有些情況下,實際讀到的位元組數(返回值)會小於請求讀的位元組數count
例如:讀常規檔案時,在讀到count個位元組之前已到達檔案末尾。
例如,距檔案末尾還有30個位元組而請求讀100個位元組,則read返回30,下次read將返回0
示例
const char * file = "hello,world\n";
int main()
{
//int open(constchar*pathname,intflags);
//int open(constchar*pathname,intflags,mode_tmode);
//返回值:成功則返回檔案描述符,否則返回-1
int fd = open("myfile", O_RDWR | O_CREAT, 0664);
if(fd < 0)
{
perror("open");
return -1;
}
char * msg = "world,hello\n" ;
ssize_t sw = write(fd, msg, strlen(file));
printf("sw = %d\n", sw);
if(sw < 0)
{
perror("write");
return -1;
}
printf("寫入成功\n");
char buf[13];
ssize_t sr = read(fd, buf, 12);
printf("sr = %d\n", sr);
if( sr <= 0 )
{
perror("read error");
return -1;
}
printf("讀取成功\n");
printf("buf = %s\n", buf);
close(fd);
return 0;
}
像這樣, 在開啟一個檔案後, 用write寫入, 然後立即用read讀取, 此時read會返回0
因為此時檔案的讀取位置已經到了末尾
如何解決這個問題呢
有一個函式
lseek
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int filde,off_t offset ,int whence);
函式說明
每一個已開啟的檔案都有一個讀寫位置,當開啟檔案時通常其讀寫位置是指向檔案開頭,若是以附加的方式開啟檔案(如O_APPEND),則讀寫位置會指向檔案尾。
當read()或write()時,讀寫位置會隨之增加,lseek()便是用來控制檔案讀寫位置的
- 引數fildes 為已開啟的檔案描述詞
- 引數offset 為根據引數whence來移動讀寫位置的位移數
Offset:偏移量,每一讀寫操作所需要移動的距離,單位是位元組的數量,可正可負(向前移,向後移)
引數
whence為下列其中一種:(SEEK_SET,SEEK_CUR和SEEK_END和依次為0,1和2).
- SEEK_SET 將讀寫位置指向檔案頭後再增加offset個位移量。
- SEEK_CUR 以目前的讀寫位置往後增加offset個位移量。
- SEEK_END 將讀寫位置指向檔案尾後再增加offset個位移量。
當whence 值為SEEK_CUR 或SEEK_END時,引數offet允許負值的出現。
下列是較特別的使用方式:
1) 欲將讀寫位置移到檔案開頭時: lseek(int fildes,0,SEEK_SET);
2) 欲將讀寫位置移到檔案尾時: lseek(int fildes,0,SEEK_END);
3) 想要取得目前檔案位置時: lseek(int fildes,0,SEEK_CUR);
返回值
當呼叫成功時則返回目前的讀寫位置,也就是距離檔案開頭多少個位元組。
若有錯誤則返回-1,errno 會存放錯誤程式碼。
可能設定erron的錯誤程式碼:
- EBADF: fildes不是一個開啟的檔案描述符。
- ESPIPE:檔案描述符被分配到一個管道、套接字或FIFO。
- EINVAL:whence取值不當。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
ssize_t read(int fd, void * buf, size_t count);
//從fd裡讀取count大小的資料到buf中
//ssize_t write(int fd, const void *buf, size_t nbyte);
ssize_t write(int fd, const void * buf, size_t nbyte);
//把buf中的nbyte大小的資料寫入fd
const char * file = "hello,world\n";
int main()
{
//int open(constchar*pathname,intflags);
//int open(constchar*pathname,intflags,mode_tmode);
//返回值:成功則返回檔案描述符,否則返回-1
int fd = open("myfile", O_RDWR | O_CREAT, 0664);
if(fd < 0)
{
perror("open");
return -1;
}
char * msg = "world,hello\n";
ssize_t sw = write(fd, msg, strlen(file));
printf("sw = %d\n", sw);
if(sw < 0)
{
perror("write");
return -1;
}
printf("寫入成功\n");
sleep(1);
//off_t lseek(int filde,off_t offset ,int whence);
//每一個已開啟的檔案都有一個讀寫位置,當開啟檔案時通常其讀寫位置是指向檔案開頭,
//若是以附加的方式開啟檔案(如O_APPEND),則讀寫位置會指向檔案尾。
//當read()或write()時,讀寫位置會隨之增加,lseek()便是用來控制該檔案的讀寫位置。
//引數fildes 為已開啟的檔案描述詞,引數offset 為根據引數whence來移動讀寫位置的位移數。
//Offset:偏移量,每一讀寫操作所需要移動的距離,單位是位元組的數量,可正可負(向前移,向後移)。
lseek(fd, 0, SEEK_SET); //令讀寫位置指向開頭
char buf[13];
//while(1)
{
ssize_t sr = read(fd, buf, 12);
printf("sr = %d\n", sr);
//ssize_t read(int handle, void *buf, int nbyte);
if( sr <= 0 )
{
perror("read error");
return -1;
}
printf("讀取成功\n");
}
printf("buf = %s\n", buf);
//close(fd);
return 0;
}