討論區快速選單
知識庫快速選單
傑米的攝影旅遊筆記 政府補助!資策會APP就業班 最新Microsoft免費研討會行事曆
[ 回上頁 ] [ 討論區發言規則 ]
用c實作檔案讀取
更改我的閱讀文章字型大小
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/2 下午 01:15:19
小第一開始是用c++ string和getline去讀取檔案中一行的字串
但是小弟想用c去讀取檔案中的字串,因此寫了下面的程式碼,
我的做法是先用fgetc找出一行中最多的字元數目,之後再用fgets去讀取一行字串
,想跟大家討論並且找出更好的方法去精進這段程式碼.

#include <stdio.h>
#include <limits.h>

int MAX_COL(FILE *fp)
{
     char ch;
     int row = 0 , col = 0 , Max_col =INT_MIN;
     while((ch=fgetc(fp))!=EOF)
     {
     ++col;//count the number of char
     if(ch=='\n')
     {
     if(col > Max_col)
     {
     Max_col = col-1;
     }
     col = 0 ;//initial
     ++row;
     }
     }
     //printf("row:%d , Max_col:%d\n",row , Max_col);
     return Max_col;
}
int main(int argc, char *argv[])
{
     FILE *Fp ;
     int max_col =0;
     if((Fp=fopen(argv[1], "r"))==NULL)
     {
     printf("file cannot be opened\n");
     return 0;
     }
     max_col = MAX_COL(Fp);
     fclose(Fp);
     char str[max_col];
     Fp=fopen(argv[1], "r");
     while(!feof(Fp))
     {
     if(fgets(str,max_col,Fp)!=NULL)
     {
     printf("%s",str);
     }
     }
     fclose(Fp);
     return 0;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/2 下午 04:55:25
#include <stdio.h>
#include <string.h>
#include <limits.h>

int MAX_COL(FILE *fp)
{
    char ch;
    int col = 0 , Max_col =INT_MIN;
    while((ch=fgetc(fp))!=EOF)
    {
    ++col;//count the number of char
    if(ch=='\n')
    {
    if(col > Max_col)
    {
    //Max_col = col-1;
    Max_col = col;
    }
    col = 0 ;//initial
    }
    }
    //printf("Max_col:%d\n",Max_col);
    return Max_col+1;//consider '\0'
}
int main(int argc, char *argv[])
{
    FILE *Fp ;
    int max_col =0;
    //char pch;
    if((Fp=fopen(argv[1], "r"))==NULL)
    {
    printf("file cannot be opened\n");
    return 0;
    }
    max_col = MAX_COL(Fp);
    fclose(Fp);
    char str[max_col];
    Fp=fopen(argv[1], "r");
    while(!feof(Fp))
    {
    if(fgets(str,max_col,Fp)!=NULL)
    {
    if(str[strlen(str)-1] == '\n')
    {
    str[strlen(str)-1] = '\0';
    }
    printf("%s\n",str);
    /*pch = strtok (str,",.-");
    while (pch != NULL)
    {
    printf ("%s\n",pch);
    pch = strtok (NULL, ",.-");
    }*/
    }
    }
    fclose(Fp);
    return 0;
}


作者 : cromayen2000(CROMAYEN2000) OpenGL卓越專家貼文超過500則人氣指數超過10000點
[ 貼文 641 | 人氣 22308 | 評價 2260 | 評價/貼文 3.53 | 送出評價 37 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/5 上午 04:24:16
如果只是要顯示出檔案中的文字內容 這樣就可以了

int main(int argc, char *argv[]) {
    FILE *fptr ;
    // 內容緩衝區長度 128
    char str[128];
    // 開啟檔案
    if( (fptr=fopen(argv[1], "r") ) == NULL ) {
     printf("file cannot be opened\n");
     return 0;
    }
    // 檢查檔案是否結束
    while(!feof(fptr)) {
     // fgets 讀入一行文字長度限定 128 個字元
     // fgets 當一行長度超過 128 字元 則讀入 127 字元並補上一個 '\0'
     // fgets 當一行長度未滿 128 字元 則全部讀入(包含尾端的 '\n')
     if(fgets(str,128,fptr)!=NULL) {
printf("%s",str);
}
    }
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/5 上午 11:19:54
此程式的由來是讀取檔案內容時,都會碰到一個問題,每行的字元數目並不相同.
因此fgetc先算出每行所需的最大的字元數目,算出後,我再用fgets去讀取每行的檔案內容.
之後得到的字元陣列內容去做處理.可能要切割或找出子字串.
換句話說,我希望字元陣列的大小是動態變動,並且希望大家提供一些意見,改進此方法和此法有何不妥之處.

ps:
c的字串還是字元陣列的觀念,所以不用字串去論述我的內容
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2079 | 人氣 89850 | 評價 9950 | 評價/貼文 4.79 | 送出評價 78 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/5 下午 12:44:58
最簡單的方法, 就是先估計一個比每行字串長度都要大的數目 (例如 4096), 然後準備一個該數目大小的 memory block, 字串讀了出來之後, 才按字串長度, 配置和複製到另一 memory block. 當然, 這個方案不是十分可行的, 因為 估計的數目不準確, 然而 你又必須先把所有資料讀一次 才能確定最長那一行的字串長度.

可行的方案有:

你可以把整個檔案都讀到記憶體, 然後再作分析, 如此, 你愛做甚也可以了.

或, 你可以用 STL 的 vector template class, 這就不用顧慮記憶體大小的問題.
作者 : cromayen2000(CROMAYEN2000) OpenGL卓越專家貼文超過500則人氣指數超過10000點
[ 貼文 641 | 人氣 22308 | 評價 2260 | 評價/貼文 3.53 | 送出評價 37 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人angleevil註記此篇回應為很有道理 2009/10/5 下午 12:52:15
分成幾個部分

第一個計算單行最大字數

一個一個字元數速度太慢了如果記憶體很吃緊的話
一次讀一段 檢查是不是讀取完整了沒有的話繼續讀完並且長度累加
紀錄最大的長度

int StrLenMax = 0;
int StrLenNow = 0;
int DataSize = 0;
char Buffer[128];
while( !feof(fptr) ) {
StrLenNow = 0;
do{
fgets(Buffer,128,fptr);
DataSize = strlen(Buffer);
StrLenNow += DataSize ;
}while( DataSize == 127 );
if( StrLenNow > StrLenMax )
StrLenMax = StrLenNow ;
}

如果記憶體不吃緊的話
直接計算整個檔案的長度
fseek( input, 0, SEEK_END);
filelen = ftell( input );
fseek( input, 0, SEEK_SET);

第二部分
>此程式的由來是讀取檔案內容時,都會碰到一個問題,每行的字元數目並不相同.
>因此fgetc先算出每行所需的最大的字元數目,算出後,我再用fgets去讀取每行的檔案內容.
>之後得到的字元陣列內容去做處理.可能要切割或找出子字串.
>換句話說,我希望字元陣列的大小是動態變動,並且希望大家提供一些意見,改進此方法和此法有何不妥之處.

c & c++的陣列是不能在執行階段變動大小的 編譯的時候就直接決定長度了 所以不用指望直接用陣列就可以解決了
這個時候就需要透過資料結構來處理了 先透過第一部分的方法 找出你的緩衝區應該要多大
然後透過 malloc or new 配置一塊你需要的緩衝區然後一行一行讀進來處理
看你的需要還可以配合連接串列把整個文件重組起來




作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/5 下午 02:31:24
恩,我先造著上面大大的教法,一段一段讀取,至於第二部分,我會在想想

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/5 下午 02:45:58
我把改進的方法放入網誌
http://www.wretch.cc/blog/hgsfhevil/11408511
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人angleevil註記此篇回應為很有道理 2009/10/5 下午 09:48:26
>我把改進的方法放入網誌
>http://www.wretch.cc/blog/hgsfhevil/11408511

〔上面的 URL 有錯誤〕

看了你的程式, 有幾點錯誤.

1. 在 MAX_COL() , 變數 'ch' 應該用 int 類型.

因為 fgetc() 的回傳類型是 int. 為什麼 fgetc() 要回傳 int 類型? 因為只有這樣才能回傳所有可能的字元值及錯誤訊息.

int 的範圍是 [INT_MIN, INT_MAX], 合法字元的範圍是 [0, CHAR_MAX], 是 int 範圍的 subset. EOF 訊息是在合法字元的範圍外. 所以你一定要用 int 來接受, 才可以保證是正確的訊息.

你的程式會 work, 因為你只把它用在讀取 ASCII 的文字檔上. 但 fgetc() 是可以用來讀任何字元的, 包括 binary data. 如果某個字元的值是 0xFF, 而如果 char signed 的話, ch = fgetc() 會得到 -1. 就會得到 EOF 的誤判.

2. 在 main() , 這行定義:
    char str[max_col];
 是錯誤的. 語言標準不允許這樣的語法. 也許有些編譯器允許, 不過是當做標準的延伸.

3. 在大多數的情況下, while(!feof(Fp)) { ... } 的寫法是錯誤的. C 函式庫 EOF 的判斷只有在實質上讀超出 end of file 的時候才會發生, 而不是「下一次讀會不會發生 EOF 錯誤」.

假定 <EOF> 是個 marker, 即使讀了最後一筆資料, 只要不超出這個 marker, EOF 錯誤就不會發生.

比方說

  目前狀況:
    <最後一筆資料><EOF>
    ↑
   File Pointer

  讀資料:
    <最後一筆資料><EOF>
            ↑
          File Pointer

  這時, EOF 錯誤還不會發生. feof() 還是傳回 0.

  只有當再試圖讀多一次資料的時候:
    <最後一筆資料><EOF>
            ↑–––→↑
          File Pointer
  才會發生 EOF 錯誤. 這時 feof() 才會傳回 nonzero.

由此可看出, 如果你只有一筆資料的話, 必須讀兩次才能判斷 EOF. 所以如果用 while (!feof()) 來 loop 的話, 它會 loop 多一次.

大部份的輸入函式都會回傳包括 EOF 的錯誤訊息, 可以直接利用這個訊息來判斷, 而不需使用 feof(). 以你的程式來說, 你原來的 while(!feof()) 還是會 loop 多一次. 但由於你在 loop 裡面有測試 fgets() 的回傳值, 所以最後一筆資料至少不會處理兩次.

實際上, 直接把 fgets() 的部份放到 while() 奡N可以了:
  while (fgets(str, max_col, Fp))
  {
    ...
  }


以下是建議:
1. 在使用 argv[1] 前, 最好先檢查 argc 的值.

2. 在呼叫完 MAX_COL() 後, 可以用 rewind(). 這樣, 檔案就不需要關閉後再重新開啟.

3. strlen() 的複雜度是 O(N), 可能的話, 減少它的呼叫:
  while (...)
  {
    const size_t len = strlen(str) - 1
    if (str[len] == '\n')
    {
      Str[len] = '\0';
    }
  }

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/6 上午 09:51:57
根據上面大大的建議,我再次修改讀取的方法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int MAX_COL(FILE *fptr)//更改的部分,沿用之前大大給我的建議
{
    int StrLenMax = 0;
    int StrLenNow = 0;
    int DataSize = 0;
    char Buffer[128];
    while( !feof(fptr) )
    {
    StrLenNow = 0;
    do{
    fgets(Buffer,128,fptr);
    DataSize = strlen(Buffer);
    StrLenNow += DataSize ;
    }
    while( DataSize == 127 );
    if( StrLenNow > StrLenMax )
    {
    StrLenMax = StrLenNow ;
    }
    }
    return StrLenMax;//consider \n
}
int main(int argc, char *argv[])
{
    FILE *fp ;
    int max_col =0;
    if(argc != 2)//更改的部分
    {
    printf("%s file.in\n",argv[0]);
    }
    else
{
    if((fp=fopen(argv[1], "r"))==NULL)
    {
    printf("file cannot be opened\n");
    return 0;
    }
    max_col = MAX_COL(fp);
    rewind(fp);
    char *str = (char *)malloc(max_col*sizeof(char));//更改的部分
    while(fgets(str,max_col,fp))//更改的部分
    {
    const size_t len = strlen(str) - 1;//更改的部分
    if(str[len] == '\n')
    {
    str[len] = '\0';
    }
    printf("%s\n",str);
    }
    free(str);
    fclose(fp);
    }
    return 0;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/6 下午 12:11:51
我把MAX_COL,修改一下,避免多讀一次

int MAX_COL(FILE *fptr)
{
     int StrLenMax = 0;
     int StrLenNow = 0;
     int DataSize = 0;
     char Buffer[128];
     while( fgets(Buffer,128,fptr) )
     {
     DataSize = strlen(Buffer);
     StrLenNow += DataSize;
     if(DataSize != 127)
     {
     if( StrLenNow > StrLenMax )
     {
     StrLenMax = StrLenNow ;
     }
     StrLenNow = 0;
     }

     }
     return StrLenMax;//consider \n
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/6 下午 02:58:57
不過我發現這樣寫,有個問題
我先找出每行中最大的字元數,再讀取字元陣列(並且把最後位置的\n換成\0)
,但是當碰到某一行擁有最大字元數時,我把它列印出來,會多印一行
最後我是把最大字元數加1,才解決這問題,
可是我不是很清楚這理由所在.

例如:
test.txt
adj;sj;dsj jdslajd fdlkj d\n
\n
=================
out.txt
adj;sj;dsj jdslajd fdlkj d\n
\n
\n

ps:
我是用linux系統,他的換行符號只有\n

修改的地方在這
int MAX_COL(FILE *fptr)
{
     int StrLenMax = 0;
     int StrLenNow = 0;
     int DataSize = 0;
     char Buffer[128];
     while( fgets(Buffer,128,fptr) )
     {
     DataSize = strlen(Buffer);
     //printf("%s->%d\n",Buffer,DataSize);
     StrLenNow += DataSize;
     if(DataSize != 127)
     {
     if( StrLenNow > StrLenMax )
     {
     StrLenMax = StrLenNow ;
     }
     StrLenNow = 0;
     }

     }
     rewind(fptr);
     return StrLenMax+1;//修改這邊
}
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/6 下午 08:51:46
>不過我發現這樣寫,有個問題
>我先找出每行中最大的字元數,再讀取字元陣列(並且把最後位置的\n換成\0)
>,但是當碰到某一行擁有最大字元數時,我把它列印出來,會多印一行
>最後我是把最大字元數加1,才解決這問題,
>可是我不是很清楚這理由所在.

請去詳細讀讀 fgets() 的說明.

fgets(buf, n, ...) 只能讀入最多 n-1 個字元, 從 buf[0] 到 buf[n-2]. 因為必須保留一個字元作終結字元.

如果最長行數的內容是: 1234\n
如果你的 MAX_COL() 傳回 5, 哪 fgets(..., 5, ...) 只能讀前面 4 個字元.

你 main() 堛 if (str[len] == '\n') block 不會執行, 你在後面加個 else block 就知道問題所在了.

然後你輸出:
  printf("%s\n", str);

第一個 '\n' 是你 printf() 的 '\n'.

輸出後重複 loop, 這時會繼續讀上次讀剩的 "\n". 結果你知道了.

所以如果你不去做多餘的把 '\n' 換成 '\0', 然後又在輸出那裡加 '\n', 那就一點問題都沒有 (至少從輸出那裡看不出問題所在).

如果你只是純粹輸出檔案內容, MAX_COL() 不需要; '\n' 轉成 '\0' 不需要; 輸出也不需要加 '\n'.

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/7 上午 08:09:30
把fget的特性考慮進去,所以我把MAX_COL輸出的大小加一
把最後的\n轉換成\0是發現這樣輸出,比較好處理
我記得c++ getline儲存到string也會把換行符號處理掉
我是模仿這特性.^^
其實我最近也寫了substr函式,find的函式還在想.
^^

char * SubStr(char *line,size_t size,size_t start,size_t len)
{
     char *pos = line + start ;
     char *temp = (char *)malloc( len*sizeof(char));
     strncpy (temp,pos,len);
     if(size == 0)
     {
     free(temp);
     temp = '\0';
     }
     return temp;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/7 上午 08:50:25
這是find的寫法,這些我督寫得很簡略,並沒有很詳細考慮到安全性的問題
我撰寫這些的目的,其實是我想實作出getline和string一些功能,當然這些功能是我以前常用的
例如:getline,substr,find.
有考慮不周的地方,請大家指教.
size_t Find( const char *StrPtr1 , size_t len , const char *StrPtr2 , size_t pos)
{
    if(len != 0)
    {
    StrPtr1 = StrPtr1 + pos;
    char* result = strstr( StrPtr1, StrPtr2 );
    StrPtr1 = StrPtr1 - pos;
    return result - StrPtr1;
    }
    else
{
    return '\0';
    }
}

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/7 上午 10:19:52
>char * SubStr(char *line,size_t size,size_t start,size_t len)

可以考慮:
char *SubStr(const char *szString, const size_t nStart, const size_t nLength, char* szSubStr);

存放 substring 的記憶體由呼叫者提供, szSubStr 指向這個記憶體.

nLength 包括終結字元.

回傳類型如果是 char*, 可以傳回指向 substr 的指標, 或 NULL 表示參數有錯誤. 如果這些都不需要, 可以用 void.

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/7 下午 02:15:47
照著R大的建議,更改了程式碼,但是不知道擷取超出長度範圍的子字串,要怎麼顯示錯誤,請問要如何更改
例如:
in.txt
1234\n
\n
我取1~到2的位置<==SubStr(str,1,2,sub);
out.txt//輸出的檔案
23\n
\n
=============程式碼如下=====================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int MAX_COL(FILE *fptr);//未更改,所以沒有顯示出來
char *SubStr(char *szString, const size_t nStart,size_t nLength, char* szSubStr)
{
     if(szString[0] != '\0')
     {
     char *ptr = szString + nStart;
     strncpy (szSubStr,ptr,nLength);
     return ptr;
     }
     else
{
     szSubStr = NULL;
     return NULL;
     }
}
int main(int argc, char *argv[])
{
     FILE *fp ;
     int max_col =0;
     if(argc != 2)
     {
     printf("%s file.in\n",argv[0]);
     }
     else
{
     if((fp=fopen(argv[1], "r"))==NULL)
     {
     printf("file cannot be opened\n");
     return 0;
     }
     max_col = MAX_COL(fp);
     char *str = (char *)malloc(max_col*sizeof(char));
     while(fgets(str,max_col,fp))
     {
     const size_t len = strlen(str);
     if(str[len-1] == '\n'){str[len-1] = '\0';}
     char *sub = (char *)malloc( (10 + 1)*sizeof(char));//consider 終結字元
     SubStr(str,21,10,sub);
     printf("%s\n",sub);
     free(sub);
     }
     free(str);
     fclose(fp);
     }
     return 0;
}

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/7 下午 07:42:21
我想R大的建議是這樣吧!,但是當擷取過長的字串時或是擷取空字串的時後,
這樣寫無法顯示錯誤,這方面要怎麼修改?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int MAX_COL(FILE *fptr);
char *SubStr(char *szString, const size_t nStart,size_t nLength, char* szSubStr)
{
     strncpy (szSubStr,szString + nStart,nLength);
     szSubStr[nLength] = '\0';
     return szSubStr;
}
int main(int argc, char *argv[])
{
     FILE *fp ;
     int max_col =0;
     if(argc != 2)
     {
     printf("%s file.in\n",argv[0]);
     }
     else
{
     if((fp=fopen(argv[1], "r"))==NULL)
     {
     printf("file cannot be opened\n");
     return 0;
     }
     max_col = MAX_COL(fp);//printf("%d\n",max_col);
     char *str = (char *)malloc(max_col*sizeof(char));
     while(fgets(str,max_col,fp))
     {
     const size_t len = strlen(str);
     if(str[len-1] == '\n')
     {
     str[len-1] = '\0';
     }//printf("%s\n",str);
     char *sub = (char *)malloc( (10 + 1)*sizeof(char));//consider 終結字元
     SubStr(str,21,10,sub);
     printf("%s\n",sub);
     free(sub);
     }
     free(str);
     fclose(fp);
     }
     return 0;
}
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人angleevil註記此篇回應為很有道理 2009/10/7 下午 09:16:32
>我想R大的建議是這樣吧!,但是當擷取過長的字串時或是擷取空字串的時後,
>這樣寫無法顯示錯誤,這方面要怎麼修改?

意思大概是這樣. 當時沒想太多, 有不足, 錯漏之處請見諒.

我個人認為「擷取過長字串」應該不算錯誤. 你只需要在這個函式的 documentation 上註明: 擷取至 szString 的終結字元或 nLength 長度的字串.

「擷取空字串」可以在幾個情況下發生, szString 是空字串, 或 nLength 等於 0. 這幾個情況本身應該也不能算是錯誤.

我想唯一的錯誤應該是當 nStart 超出 szString 的長度. 這也包括了 szString 是空字串及 nStart 大於 0 的情況.

>
>#include <stdio.h>
>#include <stdlib.h>
>#include <string.h>
>#include <limits.h>
>int MAX_COL(FILE *fptr);
>char *SubStr(char *szString, const size_t nStart,size_t nLength, char* szSubStr)
>{

在 strncpy() 前需要檢查 nStart 是否會超出 szString 的長度.

> strncpy (szSubStr,szString + nStart,nLength);
> szSubStr[nLength] = '\0';

這個錯了. 應該是 szSubStr[nLength - 1]

> return szSubStr;

關於回傳類型或值, 這就隨你了. 之前說的回傳 szSubStr 是一個可能性, 另一個可能性是回傳指向 szString 字串內, 擷取後下一個字元的指標. 在某些運用下也許會比較方便.

你想傳回一個 size_t 數值, 顯示擷取的字元數目也行.

給個例子:
  const char *SubStr(const char *szString, size_t nStart, const size_t nLength, char *szSubStr)
  {
    for (; *szString && nStart; ++szString, --nStart);

    if (nStart)
      return NULL;

    for (size_t n = 0; *szString && n < nLength; ++n)
    {
      *szSubStr++ = *szString++;
    }
    *szSubStr = '\0';
    return szString;
  }

為了方便, 這裡把 nStart 的 const 拿掉.

測試程式:
  void TestSubStr(const char *szString, const size_t nStart, const size_t nLength, char *szSubStr)
  {
    printf("\"%s\"\n", szString);
    const char *p = SubStr(szString, nStart, nLength, szSubStr);
    printf("substring = \"%s\"\n", szSubStr);
    printf("SubStr() returns = ");
    if (p)
      printf("\"%s\"\n\n", p);
    else
      printf("NULL\n\n");
  }

  int main()
  {
    char szSubStr[20];
    char *szString = "A quick brown fox jumps over the lazy dog";

    TestSubStr(szString, 5, 5, szSubStr);
    TestSubStr(szString, 30, 20, szSubStr);
    TestSubStr(szString, 0, 0, szSubStr);
    TestSubStr(szString, 100, 5, szSubStr);
    TestSubStr(szString, 100, 0, szSubStr);
  }


作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/8 上午 10:20:22
這是我寫的find的程式碼,但是當找不到此字串時,我知道c++有string::npos可以當判斷,c有什麼類似的判斷嗎?
我是先用-134521200當判斷條件.
請問大家還有什麼做法嗎?
size_t Find( const char *StrPtr1 , size_t len , const char *StrPtr2 , size_t pos)
{
     char* result = strstr( StrPtr1 + pos, StrPtr2 );
     StrPtr1 = StrPtr1 - pos;
     if( (result - StrPtr1) == -134521200 )
     {
     printf("Not find this string\n");
     exit(1);
     }
     return result - StrPtr1;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/8 上午 11:00:10
我去查了strstr的用法,他沒有找到字串會傳回NULL,所以我用這個當判斷
size_t Find( const char *StrPtr1, const char *StrPtr2 , size_t pos)
{
     char* result = strstr( StrPtr1 + pos, StrPtr2 );
     StrPtr1 = StrPtr1 - pos;
     if( result == NULL )
     {
     printf("Not find this string\n");
     exit(1);
     }
     return result - StrPtr1;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/8 下午 07:48:47
http://www.wretch.cc/blog/hgsfhevil
我把程式碼放入這blog,歡迎大家指教,
讓這個程式效能和安全性越來越好


作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/8 下午 08:58:52
>這是我寫的find的程式碼,但是當找不到此字串時,我知道c++有string::npos可以當判斷,c有什麼類似的判斷嗎?

std::string::npos 的類型是 std::string::size_type (一般等於 size_t), 數值是 -1. 所以你可以傳回 -1.

或者用 signed long.

或是傳回指標, 指向被搜尋字串, 符合搜尋字串的第一個字元, 或是 NULL. 也就是 strstr() 所傳回的指標.

若要傳回 position, 可以寫意個 FindPos(), 把 Find() 的指標轉為 position.

這是我自己的 string search library 的寫法. 裡面有大概 30 個不同用途的 Find*(), 及相對應的 FindPos*().

作者 : stubandans0913(andans)
[ 貼文 6 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/10/16 下午 06:51:28
看了此篇文章真是受益良多阿,感謝各位大大
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/1/23 下午 04:46:47
因為覺得算出最大的行數,再去讀取每一行的長度很麻煩
我改成先算出一行的長度,在讀取此行的內容,接著再取下一行的內容,
希望大家可以指教
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int main(int argc, char *argv[])
{
     if(argc != 2)
     {
     printf("%s file.in\n",argv[0]);
     }
     else
{
     FILE *fp ;
     int StrLenNow = 0 , DataSize = 0 , FilePos = 0;
     char Buffer[128];
     if((fp=fopen(argv[1], "r"))==NULL)
     {
     printf("file cannot be opened\n");
     return 0;
     }
     while( fgets(Buffer,128,fp) )
     {
     DataSize = strlen(Buffer);
     StrLenNow += DataSize;
     if(DataSize != 127)
     {
     fseek(fp,FilePos,SEEK_SET);
     char *str = (char *)malloc( (StrLenNow+1) * sizeof(char) );
     fgets(str,StrLenNow+1,fp);
     const size_t len = strlen(str);
     if(str[len-1] == '\n')
     {
     str[len-1] = '\0';
     }
     printf("%s\n",str);
     FilePos += StrLenNow;
     StrLenNow = 0;
     free(str);
     }
     }
     fclose(fp);
     }
     return 0;
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/1/23 下午 05:20:30
其實我現在最怕的問題是某一行的長度剛好是127的倍數,這樣會有問題
不知道大家可以給我建議,解決這問題嗎?
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/1/25 上午 11:16:23
後來想到fgetc會自己判斷\n,所以不會有上述的問題
@@拍謝
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/11 下午 08:30:03
我把之前的讀取檔案的程式改成函式去讀取每行的字段
但是卻產生記憶體區段錯誤,請問大家要如何解決?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

bool GetLine( FILE * fp , char * str );
int main(int argc, char *argv[])
{
     if(argc != 2)
     {
     printf("%s file.in\n",argv[0]);
     }
     else
{
     FILE * fp ;
     char * line = NULL;
     if((fp=fopen(argv[1], "r"))==NULL)
     {
     printf("file cannot be opened\n");
     return 0;
     }
     while(GetLine(fp,line))
     {
     printf("%s\n",line);//記憶體區段錯誤
     }
     fclose(fp);
     }
     return 0;
}

bool GetLine( FILE * fp , char * str )
{
     bool bDone = false;
     int StrLenNow = 0 , DataSize = 0;//StrLenNow用來統計每一行的字數
     static int FilePos = 0;
     char Buffer[128];

     while( ( bDone = fgets(Buffer,128,fp) ) )
     {
     DataSize = strlen(Buffer);
     StrLenNow += DataSize;
     if(DataSize != 127)
     {
     free(str);//刪除之前的資料
     fseek(fp,FilePos,SEEK_SET);
     str = (char *)malloc( (StrLenNow+1) * sizeof(char) );
     fgets(str,StrLenNow+1,fp);
     const size_t len = strlen(str);
     if(str[len-1] == '\n')
     {
     str[len-1] = '\0';
     if(str[len-2] == '\r')//針對dos的段行
     {
     str[len-2] = '\0';
     }
     }
     FilePos += StrLenNow;
     StrLenNow = 0;//歸零
     return bDone;
     }
     }
}
作者 : sleepyfish(愛睏魚) C#優秀好手C++優秀好手貼文超過500則
[ 貼文 523 | 人氣 0 | 評價 2890 | 評價/貼文 5.53 | 送出評價 13 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/12 下午 09:27:47
你的 bool GetLine( FILE * fp , char * str ) 由 caller 傳入單純的 pointer,
然後在 GetLine 中 str = (char *)malloc( (StrLenNow+1) * sizeof(char) )

用了 malloc(),卻沒有相對應的 free() ........... 多進出 GetLine() 幾次馬上就
會出現 memory leak 的問題了。
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/13 下午 12:05:21
free(str);//刪除之前的資料<==這個有考慮到,只是格式跑掉,不容易看

朋友建議用char **str 來解決.

其實真的想問是如何傳遞一維指標的參數
畢竟我對指標觀念很薄弱,
本來以為一維指標,只要用一維指標當參數型態就好.
~"~可以有例子教導我這點嗎?
作者 : affair(Affair)
[ 貼文 14 | 人氣 0 | 評價 200 | 評價/貼文 14.29 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/13 下午 01:33:10
我估計記憶體區段錯誤的原因是 : free(str) 在第一次 loop 的時候,free() 了不應 free() 的指標。
free() 是一定要有 malloc() 了記憶體才用,如果指標並没有 malloc() 記憶體,是不可 free() 的。否則在 linux 底下便會出 segmentation fault (記憶體區段錯誤)。
另外,(據我所知) 純 C 語言是没有 boolean (C++ 才有),應用 0 或 1 代替。
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/13 下午 04:27:07
其實free放在那個位置,並沒有影響.
純c的確是沒有boolen.所以我是用c++ compiler.(畢竟我是用c++的)

而且測試過後,出問題的是printf("%s\n",line);
函式中的str的字串並未回傳給line.

朋友建議我改成==>
(1)bool GetLine( FILE * fp , char ** str );
(2)GetLine(fp,&line);//line的宣告是char *

因此我覺得是指標傳遞的觀念不清楚,而導致這問題








作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/13 下午 04:27:36
其實free放在那個位置,並沒有影響.
純c的確是沒有boolen.所以我是用c++ compiler.(畢竟我是用c++的)

而且測試過後,出問題的是printf("%s\n",line);
函式中的str的字串並未回傳給line.

朋友建議我改成==>
(1)bool GetLine( FILE * fp , char ** str );
(2)GetLine(fp,&line);//line的宣告是char *

因此我覺得是指標傳遞的觀念不清楚,而導致這問題








作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/14 上午 12:09:05
我po上修改好的程式,
當初撰寫的動機是練習指標和讀取檔案的內容(因為我很討厭dos和linux的換行問題,希望自己寫成一個getline來解決)
現在我困在指標參數的傳遞觀念,希望大家不令指教

===========略============
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("%s file.in\n",argv[0]);
}
else
{
===========略============
     char * line;
while(GetLine(fp,&line))
{
printf("%s\n",line);
}
fclose(fp);
}
return 0;
}

bool GetLine( FILE * fp , char ** str )
{
===========略============
while( ( bDone = fgets(Buffer,128,fp) ) )
{
DataSize = strlen(Buffer);
StrLenNow += DataSize;
if(Buffer[DataSize-1] == '\n')//判斷是否到段落
{
free(*str);//刪除之前的資料
*str = (char *)malloc( (StrLenNow+1) * sizeof(char) );

fseek(fp,FilePos,SEEK_SET);
fgets(*str,StrLenNow+1,fp);//讀取整行的段落

const size_t len = strlen(*str);
if( (*str)[len-1] == '\n' )//處理段行符號
{
(*str)[len-1] = '\0';
if( (*str)[len-2] == '\r' )//針對dos的段行
{
(*str)[len-2] = '\0';
}
}
FilePos += StrLenNow;
StrLenNow = 0;//歸零
if(!bDone)
{
FilePos = 0;
}
return bDone;
}
}
}
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3727 | 人氣 170106 | 評價 34350 | 評價/貼文 9.22 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/14 上午 07:47:03
氣... ~"~

寫了近2000多言的教學, 發送前先按下Ctrl+C備份一下, 因為以前已遇過好幾次都因為發送失敗而lost掉原文. 結果, 我沒按到Ctrl鍵, 最後只有一個c字母, 按了Ctrl-Z也無法回復原文, 心中真的是@$#!!!

沒辦法了, 去睡了. 晚一點如果有空再補一篇簡論吧.

作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/18 上午 02:33:46
殷殷期盼R大和青衫的指導文.~"~
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3727 | 人氣 170106 | 評價 34350 | 評價/貼文 9.22 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/19 下午 11:35:13
其實檔案讀取的處理, 視你的用途與效能要求, 會有各種不同的寫法.

以文字檔而言, 如果檔案不大(百MB以內), 且必須處理到每一行時, 通常最有效率的方法便是整個檔案一次讀進來, 再直接針對這個"大字串"做行切割. 視運用情況, 可將換行字元替換成NULL字元, 並回傳各行的行首指標, 這樣的速度最快. 亦可針對每一行分別allocate一塊記憶體, 再將資料拷貝過去 (會多了m次allocate/free與O(n)拷貝,m=總行數,n=檔案大小). 記錄各行行首指標的方式有3種, 一是先1 pass得到總行數, allocate好記憶體後, 再第2 pass時記錄, 這樣的速度會較慢, 但記憶體運用最佳. 一是採用類似CArray的方式記錄, 空間不足時, 改用另一塊較大記憶體, 再將舊的記憶體內容copy過去, 最後更換記憶體. 它的效能在總行數很高時, 會較慢些 (可能多出O(n^2)拷貝動作). 最後一種是採用記憶體區塊串聯法, 每次配置相同大小的記憶體記錄, 不夠時再增加一個, 邏輯上是將全部串聯在一起的記憶體區塊視為整個一大塊資料. 這種方式效能最佳, 但較費記憶體. 有時為了適合最終的運用, 也可最後再做"合併"在同一塊記憶體的動作 (多了O(n)的拷貝動作).

行切割的程式大約如下:

const char* s = 整個檔案的內容(採用binary方式讀入,注意記憶體大小=檔案大小+1,最後要補NULL字元)
while (*s != 0)
{
   const char* start = s;
   while ((*s != 0) && (*s != 0x0D) && (*s != 0x0A)) ++s;
   得到一行資料,行首指標為start,長度為s-start,看你要如何處理...
   if (*s == 0x0D) ++s;
   if (*s == 0x0A) ++s;
}

如果單純只需讀取某幾行的話, 那麼全檔讀入的效能, 顯然就較差了 (可能多了很多不必要的硬碟讀取). 或是檔案很大時, 也可能無法全部讀入記憶體. 普通的做法是一個字元一個字元慢慢讀取比較, 這種方法寫起來最簡單, 但效率極差, 一般的軟體, 幾乎都不會採用這種方法, 多半會利用緩衝區, 一次讀取一定數量的資料來處理. 緩衝區大多採用512或1024 byte, 愈接近一個sector大小愈好, 過大或過小均不宜.

採用緩衝區法, 要注意不要重複讀取. 像樓主最後的程式, 有一個地方便是重複讀取. 因為硬碟讀取的速度很慢, 既然某些資料已讀進來了, 可以利用記憶體拷貝的方式, 來減少再次檔案讀取. 至於收集緩衝區內容成一行的資料, 可以使用CArray法(CString同), 也可以使用區塊串聯法, 當然後者的方式較快, 但控制上也比較複雜. 程式sample的部份, 過幾天後如果有空再列吧.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3727 | 人氣 170106 | 評價 34350 | 評價/貼文 9.22 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/20 上午 07:47:03
上面那篇: 其實想寫第二次時, 真的覺得有點懶. 最初時候, 興之所至, 就寫了很多, 現在反而不知寫什麼比較好.

現在比較想談些題外話, 發個牢騷. 底下內容不想在程式設計甘苦談po的原因是, 個人覺得會引起太多爭議, 因此潛在這裡吧. 看過的人就當做沒看到吧.

---- 開幕式:幕不想升上來, 換篇看吧 ----

前陣子看過許多的抗議, 怪政府沒照顧他們, 有人8年的薪資從1萬8升到2萬2, 有人碩士了, 兼差3個職, 還是還不了當初的學貸. 其實我看了後, 真的是猛搖頭.

8年的薪資, 我看他從事的, 都是行政職務, 這是所有公司最低層的職務, 難怪薪資怎麼升都升不上去. 為何不考慮改做比較有技術導向的職務, 或是較機動的業務方面工作? 怕吃苦? 那怪不得人.

再說學貸. 我們那時候可沒這個東西. 家裡沒錢, 自己就得想辦法. 很多人考上了台清交成等國立大學, 沒錢, 也只能放棄 (多轉到不必錢的教育學院或軍事單位). 我家裡也沒錢, 所以寒暑假只得拼命打工賺學費 (舉凡搬運, 焊接, 電鍍, 甚至貼魔術方塊貼紙等等有的沒的工作都接), 平常上課期間就在學生餐廳洗碗盤, 賺生活費 (那時在交大, 每個月有3千元收入). 直到大二時, 連續參加幾個程式競賽都拿到第一名, 大三才有人找翻譯英文資訊書, 那時就比較好賺了, 不用再跟大一大二時那樣辛苦地用勞力賺錢.

大學畢業, 研究所前, 暑假全職的第一份正式程式設計工作, 我的薪水是1萬8, 跟別人都一樣. 即便那時寫的"畢昇表格系統"一炮而紅, 兩年後, 我的薪水還是1萬8, 所以有人挖角, 當然跳啦. 其實, 大學一畢業, 唸研究所時, 我就完全經濟獨立. 家裡不再供應金錢, 甚至我還要拿錢回家. 那時, 白天上課, 沒課就上班, 晚上便兼家教, 沒家教的時候就做資訊書翻譯, 能申請的獎學金都申請 (還好, 我的成績一向都不錯). 沒錢, 就要想方設法去賺, 而不是怨天尤人, 那真的一點意義都沒. 兼了3個差又如何? 要比誰比較累嗎?

真的靠資訊專才累積財富, 是從進捷成之後才開始, 主要是我寫的軟體, 每個都讓公司賺飽飽. 於是從最初跳槽過來的月薪2萬2一路調升, 十年後, 我的月薪已超過13萬. 每個人都會經歷過那段低檔的起薪階段, 因此起薪多少, 根本不重要. 重要的是, 你後來做了什麼讓自己加值的事? 或許是幫公司賺錢, 或許是幫公司解決了無法解決的問題, 或許是寫出了公司同業無法寫出的軟體. 無論如何, 那些都會表現出你在公司裡的"價值", 薪水也會反應在這一個"價值"上. 除非老闆無識人之明, 或是被那個無知的主管給擋掉了, 那麼也該是跳槽的時候了.

對於一個高薪跳槽者, 大部份的老闆與主管都會懷有執疑能力的想法. 一般我的作法是, 第一年只要求最低限度的薪水, 然後看我一年表現後再決定第二年的薪水. 其實懷才不遇的人, 也是可以用這招. 第一年的時間, 就是該展現實力的時候了. 如果老闆還是不賞識 (還是自己的能力不符合?), 那就再另找一家吧. 當初離開捷成另找工作時, 我將月薪降到8萬求表現, 第二年後老闆直接double留人. 有能力的人, 其實不用太擔心. 伯樂雖少, 總有相遇時.

與其不斷地抱怨懷才不遇, 薪水過低, 不如努力強化自己, 等待展翅高飛. 似乎每個行業都是相同的狀況. 劉德華, 周星馳, 任賢齊... 很多的大牌明星, 幾乎都經歷過當臨演, 求通告的苦日子. 既然沒有含金湯匙出生的先天命, 那麼就後天自己創造出那把金湯匙的運吧.

---- 閉幕式:幕迅速掉下來, 不謝收看 ----
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/20 下午 03:51:36
哀,我也來個題外話
dos和unix的換行問題真的會搞死人

我今天用vs 2005 c++去測試,
結果input和output的結果不同.
雖然我是在unix下撰寫此程式去解決不同平台的問題.

結果是...算了.當練習吧
ps:話說學校實驗的unix系統又掛了.
可能自己要找台電腦灌unix了,煩
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++曠世奇才新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4759 | 人氣 9172 | 評價 31260 | 評價/貼文 6.57 | 送出評價 138 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/21 下午 09:43:01
>哀,我也來個題外話
>dos和unix的換行問題真的會搞死人

如果要比較完整的話, 你還必須考慮舊版 Mac 的 newline 格式.

CR: 早期的 Apple 家族, 舊版 Mac OS, ...
LF: *nix, Mac OSX, ...
CR+LF: CP/M, DOS, Windows, OS/2, Symbian, Palm, ...

比較好的方法就是以 binary 的格式來讀.

如果你要一行一行的當作 text 來讀, 應該先把檔轉換成 native format. 要不然就應該用 binary 的格式來讀,把 CR 及 LF 都換成 '\0'.

主要的關鍵是: 你想要用 text 的函式來處理有可能不是 native text format 的檔, 這當然會把問題搞得複雜.


如果只考慮上面三種 newline 格式, 要轉換還算簡單. 很多年前我寫的 text convert 程式, 你可以參考一下:

轉換成 DOS (CR+LF) 格式:
  int ch, chPrev;

  chPrev = -1;
  ch = getc(fpInFile);

  while (ch != EOF)
  {
    if (!CheckNonBinary(ch))
      return false;

    if (CtrlZAtEOF(ch, fpInFile, fpOutFile))
      break;

    if (ch == LF && chPrev != CR)
    { // Unix text file: only LF
      putc(CR, fpOutFile);
    }
    putc(ch, fpOutFile);
    chPrev = ch;
    ch = getc(fpInFile);

    if (chPrev == CR && ch != LF)
    { // MAC text file: only CR
      putc(LF, fpOutFile);
    }
  }


轉換成 *nix (LF) 格式:
  int ch, chPrev;

  chPrev = -1;
  ch = getc(fpInFile);

  while (ch != EOF)
  {
    if (!CheckNonBinary(ch))
      return false;

    if (CtrlZAtEOF(ch, fpInFile, fpOutFile))
      break;

    if (chPrev == CR)
    {
      putc(LF, fpOutFile);
      if (ch != CR && ch != LF)
        putc(ch, fpOutFile);
    }
    else if (ch != CR)
      putc(ch, fpOutFile);

    chPrev = ch;
    ch = getc(fpInFile);
  }
  if (chPrev == CR)
    putc(LF, fpOutFile);


轉換成 Mac (CR) 格式:
  int ch, chPrev;

  chPrev = -1;
  ch = getc(fpInFile);

  while (ch != EOF)
  {
    if (!CheckNonBinary(ch))
      return false;

    if (CtrlZAtEOF(ch, fpInFile, fpOutFile))
      break;

    if (ch == LF && chPrev != CR)
    { // Unix text file: only LF
      putc(CR, fpOutFile);
    }
    else if (ch != LF)
    {
      putc(ch, fpOutFile);
    }
    chPrev = ch;
    ch = getc(fpInFile);
  }

在上面的程式片段裡, fpInFile 及 fpOutFile 都是 FILE*. 所有的檔都是以 binary 格式開啟的.

CheckNonBinary() 檢查 ch 是不是 ASCII.

CtrlZAtEOF() 檢查檔案的最後一個字元是不是 Ctrl-Z 控制字元 (這有歷史的原因, 不贅), 並把它刪除掉.

主要的轉換邏輯在呼叫上述兩個函式後, 這個邏輯的特性是: Source file (fpInFile) 可以是上述三個格式的任何一種.

這個專案十多年以前寫的, 應該會有可供改進的空間, 有興趣的自己研究一下.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3727 | 人氣 170106 | 評價 34350 | 評價/貼文 9.22 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/23 上午 04:17:45
誠如Raymond所提, 換行字元有3種方式. 有些人處理起來, 覺得很麻煩, 但請看我前面列的斷行程式sample, 一種寫法就可適用3種情況的讀取. 也許初看起來沒什麼, 其實裡面的寫法都是已經歷了很多的經驗才累積而成的.

讀取文字檔, 不能假設它是那個系統的斷行方式, 就像Linux可能讀到Windows的文字檔, Windows也可能讀到Unix的文字檔. 但輸出的話, 可以針對輸出的系統, 做應做的處理. 所以輸入略不同於輸出, 很平常.

許多寫法, 個人不想去詳細說明, 因為這牽涉到許多歷史的因緣, 講起來很費神. 就如Ctrl-Z (1Bh字元), 在早期時被視為文字檔資料的結束, 但如今已不再視同處理. 就像記事本, Ctrl-Z只會形式一個黑塊的文字而已, 並不是文字檔的結束, 所以我的程式裡早已去除這部份的檢驗.

write once, run everywhere, 或許可以達成. 但write once, run everytime, 真的很困難. 就像很多早期的java程式, 換了新版的java compiler, 很多地方都得重寫. compiler-oriented? data-oriented? 愈來愈好(難 )玩... ~"~
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/29 下午 05:03:04
根據青杉和R大的建言,我做了修改.

bool GetLine( FILE * fp , char* &str )
{
 bool bDone = false;
 int StrLenNow = 0 , DataSize = 0;//StrLenNow用來統計每一行的字數
 char Buffer[512];
 char *PreTmp = NULL , *tmp = NULL;
 while( ( bDone = fgets(Buffer,512,fp) ) )
 {
  if(StrLenNow != 0)
  {
   PreTmp = (char*) malloc( (StrLenNow+1) * sizeof(char) );
   strcpy(PreTmp,tmp);//handoff
   free(tmp);
  }
  
  DataSize = strlen(Buffer);
  StrLenNow += DataSize;
 
  tmp = (char*) malloc( (StrLenNow+1) * sizeof(char) );
  
  if( PreTmp != NULL )
  {
   strcpy(tmp,PreTmp);//handoff
   free(PreTmp);
  }
  
  strcpy(tmp,Buffer);//將新讀入的資料加入到temp
   
  if(Buffer[DataSize-1] == '\n' || Buffer[DataSize-1] == '\r')//判斷是否到段落
  {
   str = tmp;//handoff
   const size_t len = strlen(str);
   if( str[len-1] == '\n' || str[len-1] == '\r')
   {
    str[len-1] = '\0';
    if( str[len-2] == '\r' )
    {
     str[len-2] = '\0';
    }
   }
   StrLenNow = 0;//歸零
   return bDone;
  }
 }
}
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/13 下午 03:30:51
我之後是使用malloc新增空間和realloc去擴充空間
來解決資料再次讀取的問題,
有空我會去研究不同系統格式的問題
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/10 下午 04:36:59
這是我後來更改的作法,利用realloc去實作

bool GetLine( FILE* pF , char** pszStr )
{
    bool bDone = false;
    //nStrLenNow用來統計每一行的字數
    int nStrLenNow = 0;
    int nDataSize = 0;
    char Buffer[nBUFSIZE] ="";

    while( ( bDone = fgets(Buffer,nBUFSIZE,pF) ) )//fgets
    {
     nDataSize = strlen(Buffer);
     nStrLenNow += nDataSize;

     if ( nStrLenNow <= nBUFSIZE )
     {
     //清除之前的記憶體空間
     free(*pszStr);
     *pszStr = (char *)malloc( (nStrLenNow+1) * sizeof(char) );
     memset(*pszStr,0,nStrLenNow+1);

     if ( false == CheckMemAlloc(&(*pszStr)) )
     {
     return false;
     }

     strncpy(*pszStr,Buffer,nDataSize);
     }
     else
{
     *pszStr = (char *)realloc(*pszStr , sizeof(char) * (nStrLenNow+1));
     memset(*pszStr + (nStrLenNow - nDataSize),0,sizeof(char) * nDataSize);
     if ( false == CheckMemAlloc(&(*pszStr)) )
     {
     return false;
     }

     strncat(*pszStr,Buffer,nDataSize);
     }

     //判斷是否到段落
     if(Buffer[nDataSize-1] == '\n' || Buffer[nDataSize-1] == '\r')
     {
     (*pszStr)[nStrLenNow-1] = '\0';//處理unix斷行符號
     if( (*pszStr)[nStrLenNow-2] == '\r' )//針對dos的斷行
     {
     (*pszStr)[nStrLenNow-2] = '\0';
     }
     nStrLenNow = 0;//歸零
     return bDone;
     }
    }
    return bDone;
}
 板主 : simula
 > C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
C++
1 Raymond 12650 
2 simula 4690 
3 青衫 4670 
4 coco 3900 
5 白老鼠(Gary) 3610 
6 Ben 2250 
7 ozzy 2010 
8 Anderson 1960 
9 windblown 1650 
10 Kenny 1540 
C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2014 程式設計俱樂部 http://www.programmer-club.com.tw/
0.40625