1. 程式人生 > >C語言read函式的那些坑

C語言read函式的那些坑

  今天在複習UNIX檔案系統,用到那個read函式,但是無意中卻掉到一個坑裡了,用了一個多小時才找到問題根源,這裡記錄一下。

  問題是這樣的:我需要使用read和write函式把鍵盤輸入的資訊複製到輸出。所以我寫了如下程式:

#include<stdio.h>
#define MAXSIZE 10
int main(void)
{
    char c;
    char buf[MAXSIZE];
    int n;
    while((n = read(0,buf,MAXSIZE)) > 0)//  海燕高爾基在蒼茫的大海上狂風捲積
        write(1
,buf,n);//從buf中輸出n個位元組的資訊到標準輸出中return 0; }

  輸入”HelloWorld“檢測,好像沒問題。但是當我輸入”海燕高爾基在蒼茫的大海上狂風捲積“卻發現不太對了,這是個什麼操作??

HelloWorld
HelloWorld
海燕高爾基在蒼茫的大海上狂風捲積
海燕高爾基海上狂風捲

  為什麼後面那個它中間有幾個字掉隊了?經過多次測試我發現,如果輸入的是英文字元就沒問題,而中文字元位元組數只要超過了那個MAXSIZE就會出問題。我一度認為,是因為讀取一次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緩衝區中的位置。

  為了檢測我讀取到的到底是多少個位元組,我把程式修改如下:

#include<stdio.h>
#define
MAXSIZE 10 int main(void) { char c; char buf[MAXSIZE]; int n; while((n = read(0,buf,MAXSIZE)) >= 0)// 海燕高爾基在蒼茫的大海上狂風捲積 { printf("%d\n",n); write(1,buf,n);//從buf中輸出n個位元組的資訊到標準輸出中 putchar('\n'); } return 0; }

  我很疑惑的發現:每次讀到的的確是MAXSIZE個位元組的資料,也把這對應的資料輸出了,但是在一次迴圈後,中間還是跳過了MAXSIZE個位元組的資料沒輸出。

海燕高爾基在蒼茫的大海上狂風捲積
10
海燕高爾基
10
海上狂風捲

  

  到底是什麼導致了這一現象呢?我懷疑是字元與位元組的問題,但是說不上到底是那個函式在處理這個出問題了。所以我另外寫了一個函式測試。

#include<stdio.h>
int main(void)
{
    char buf[4];
    char c ;
    int i;
    for(i = 0;i < 3;++i)
    {
        read(0,buf,2);
        buf[2] = '\0';
        printf("%s\n",buf);

        read(0,buf,2);
        buf[2] = '\0';
        printf("%s\n",buf);
    }
}

  我三次分別輸入 ”好的\n"   "好的h"  “好hj”,其輸出如下:

好的
好



好的h
好
h

好hj
好
j

  我發現,其實在讀取的時候,資料是沒有任何問題的:一箇中文佔了兩個位元組,所以第一組測試資料的兩個位元組讀了前兩個位元組 "好" 字並輸出了,但是它下一個竟然不是讀第三個位元組和第四個位元組的 "的" 字,而是把回車讀進去並輸出了!(每次輸出都本來會輸出一個空格,這裡共輸出了四個空格)

  而第二組測試資料也是首先讀兩個位元組,輸出 "好" ,而第二次讀取的兩個位元組分別是 'h' 和 '\n' 。

  第三組資料前兩個位元組讀的是 "好" ,而後兩個位元組讀的分別是 'j' 和 '\n' 。

  其實到這裡,問題已經不難看出了,當我們使用read函式去讀取資料時,它會按照你提供的count去讀取count個位元組的資料,同時檔案指標後移,但是檔案指標移動並非是以位元組為單位來移動的!!!而是以字元為單位來移的。(這裡說的字元並非只是char型別的,還包括了寬字元。我把它們都叫字元)。所以這也就導致了我最開始的那個錯誤。

海燕高爾基在蒼茫的大海上狂風捲積
10
海燕高爾基
10
海上狂風捲

  它讀取了10個位元組的資料,也就是讀到了5箇中文字;然後它把檔案指標往後移動10個字元,所以在第一次呼叫read函式之後,檔案指標已經指向了第十一個字元,也就是 "海" 字。所以下一次讀取就是從這個位置開始了。