1. 程式人生 > >關於write和read以及檔案讀寫位置

關於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;
}

這裡寫圖片描述