2012-02-12 108 views
1

我是新來的C編程語言,並試圖通過解決只用C和它的標準libraires從項目歐拉網站的問題得​​到改善。我已經介紹了基本的C基礎知識(我認爲),函數,指針和一些基本的文件IO,但現在遇到了一些問題。從文件中讀取格式字符串到數組用C

這個問題是關於閱讀名字的文本文件和計算「名稱分數」等等等等,我知道我將使用的算法,並有大部分的程序設置,但只是不知道如何閱讀文件正確。

該文件的格式爲 「名稱一」,「Nametwo」,「比利」,「鮑比」,「坦率」 ...... 我已經搜查,搜查,並試圖無數的東西,但似乎無法閱讀這些作爲個人名字到字符串數組(我認爲這是正確的方式來存儲他們單獨?)我已經嘗試使用sscanf/fscanf與%[^ \「,]。我已經嘗試過這些函數和fgets的不同組合,但我的理解fgets是我每次打電話時都會得到一個新行,這是一個超過45,000個字符的文本文件,全部在同一行上。

我不確定我是否遇到了我對scanf函數的誤解,或者我對存儲一個字符串數組的誤解戒指去,我(想)我已經意識到,當我聲明一個字符串數組它不會爲字符串本身分配內存,我需要做的事情。但我仍然無法獲得任何工作。

這裏是我現在必須努力只是一些名字我在命令行中輸入來測試我的方法讀取的代碼。

此代碼輸入任何字符串,最多緩衝區大小(100):

int main(void) 
{ 
    int i; 
    char input[100]; 
    char* names[10]; 

    printf("\nEnter up to 10 names\nEnter an empty string to terminate input: \n"); 

    for(int i = 0; i < 10; i++) 
    { 
     int length = 0; 
     printf("%d: ", i); 
     fgets(input, 100, stdin); 
     length = (int)strlen(input); 
     input[length-1] = 0;  // Delete newline character 
     length--; 

     if(length < 1) 
     { 
     break; 
     } 

     names[i] = malloc(length+1); 
     assert(names[i] != NULL); 
     strcpy(names[i], input); 
    } 
} 

但是,我根本無法使這項工作在格式化字符串讀取。

請告訴我一下,如何與格式閱讀。我以前在輸入緩衝區中使用過sscanf,並且工作正常,但我不覺得我可以在45000 + char行上做到這一點?我認爲這是否正確?這是甚至可以接受的方式來讀取字符串到數組中?

我道歉,如果這是長的和/或不明確的,這是非常晚,我感到非常沮喪。

感謝任何人,每個人的幫助,我很期待最終成爲這個網站的活躍成員!

回答

1

實際上有這裏有兩個基本問題:

  1. 無論掃描輸入字符串這裏是正確的策略。我認爲不是因爲它可能在這個任務上工作,而是會遇到更復雜的情況,它很容易中斷。
  2. 如何處理45k字符串。

實際上,你不會遇到太多這樣大小的字符串,但它不是任何容量的現代計算機都不能輕易處理的東西。只要這是爲了學習的目的,然後迭代學習。

最簡單的第一種方法是將整個行/文件整合到合適大小的緩衝區中並自行解析。您可以使用strtok()分隔逗號分隔的令牌,然後將令牌傳遞給一個可以去除引號並返回單詞的函數。將這個詞添加到你的數組中。

對於第二次通過,您可以廢除strtok(),並通過遍歷緩衝區並自行分割逗號令牌來自己解析字符串。

最後但並非最不重要的是,您可以編寫一個版本,將較小的文件塊讀入較小的緩衝區並解析它們。這增加了處理多個讀取和管理緩衝區以解釋緩衝區末尾的半讀令牌等的複雜性。

在任何情況下,將問題分解爲塊並學習每一個細化。

編輯

#define MAX_STRINGS 5000 
#define MAX_NAME_LENGTH 30 

char* stripQuotes(char *str, char *newstr) 
{ 
    char *temp = newstr; 

    while (*str) 
    { 
     if (*str != '"') 
     { 
      *temp = *str; 
      temp++; 
     } 

     str++; 
    } 

    return(newstr); 
} 

int main(int argc, char *argv[]) 
{ 
    char fakeline[] = "\"Nameone\",\"Nametwo\",\"billy\",\"bobby\",\"frank\""; 
    char *token; 
    char namebuffer[MAX_NAME_LENGTH] = {'\0'}; 
    char *name; 
    int index = 0; 
    char nameArray[MAX_STRINGS][MAX_NAME_LENGTH]; 

    token = strtok(fakeline, ","); 
    if (token) 
    { 
     name = stripQuotes(token, namebuffer); 
     strcpy(nameArray[index++], name); 
    } 

    while (token != NULL) 
    { 
     token = strtok(NULL, ","); 

     if (token) 
     { 
      memset(namebuffer, '\0', sizeof(namebuffer)); 
      name = stripQuotes(token, namebuffer); 
      strcpy(nameArray[index++], name); 
     } 
    } 

    return(0); 
} 
+0

謝謝!這有助於噸..我仍然有一些問題,但。關於上面的#1,爲什麼這不是一個合理的策略?什麼會打破,什麼是更復雜的情況?其次,使用'strtok'完美地完成每個名字的分離,但我遇到了剝離引號的問題。在我的函數中,我想我應該明顯地將它傳遞給一個字符串的指針,該字符串是帶有引號的名稱。我對取消引號的策略以及我應該返回的內容感到困惑。一個新的字符數組的名稱?或者編輯字符串本身?謝謝! – 2012-02-13 04:11:04

+0

我覺得我得到它了,我的方法是將令牌指針傳遞給一個以名字[1]開頭的函數,並將每個char複製到一個temp char數組中,直到它到達引號。然後,在我的主'名稱[]'字符串數組中爲該臨時數組的長度分配內存,並將臨時字符串複製到主字符串數組中。它運作良好,但這是做這件事的好方法嗎?再次感謝 – 2012-02-13 04:55:39

+0

基本上就是這樣。因爲你認爲這將是一個報價,所以傳遞名稱[1]很難看。我開始用這個函數來幫助你,但是用strtok()有很多時髦的小問題,因此解釋代碼需要更長的時間。這聽起來像是大部分,所以我不認爲我毀了你的任何東西。這只是一個黑客,所以仍然有改進的空間。 – Duck 2012-02-13 06:06:40

0

fscanf("%s", input)讀取一次在一個令牌(以空格包圍的字符串)。您可以掃描輸入,直到遇到特定的「輸入結束」字符串,例如「!」,或者您可以等待文件結束信號(通過按下「Ctrl + D」實現)一個Unix控制檯或在Windows控制檯上按「Ctrl + Z」。

第一個選項:

fscanf("%s", input); 
if (input[0] == '!') { 
    break; 
} 
// Put input on the array... 

第二個選項:

result = fscanf("%s", input); 
if (result == EOF) { 
    break; 
} 
// Put input on the array... 

不管怎樣,當你一次讀取一個令牌,還有對輸入的大小沒有限制。

0

爲什麼不搜索引用字符的巨型字符串呢?事情是這樣的:

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

int main(void) 
{ 
    char mydata[] = "\"John\",\"Smith\",\"Foo\",\"Bar\""; 
    char namebuffer[20]; 

    unsigned int i, j; 
    int begin = 1; 
    unsigned int beginName, endName; 
    for (i = 0; i < sizeof(mydata); i++) 
    { 
     if (mydata[i] == '"') 
     { 
      if (begin) 
      { 
       beginName = i; 
      } 
      else 
      { 
       endName = i; 
       for (j = beginName + 1; j < endName; j++) 
       { 
        namebuffer[j-beginName-1] = mydata[j]; 
       } 
       namebuffer[endName-beginName-1] = '\0'; 
       printf("%s\n", namebuffer); 
      } 
      begin = !begin; 
     } 
    } 
} 

找到第一個雙引號,那麼第二個,然後在你的名字符串之間讀出的字符。然後根據需要針對相關問題處理這些角色。