2017-05-05 159 views
0

這個問題非常接近this topic,但我更喜歡這個解決方案所需的可讀性和指針說明。將字符串拆分爲完全動態分配的字符串數組

所以我有一個數據文件,我從它得到一個很長的字符數組。我想把這個字符串拆分成一個數組,每個字符串都與這個文件的一行對應。
我看到的解決方案,但他們都使用有限的數組,因爲我不知道每行的長度,我真的需要動態地分配他們所有,但我找不到線的長度,因爲strtok沒有把在每個字符串的末尾有空字符\0

我有現在這是什麼兩種解決方案,但沒有工作:

int get_lines(char *file, char **lines) { 
    int nb_lines = 0; 
    char *token = strtok(file, "\n"); 
    for(int i = 0; token != NULL; i++) { 
     token = strtok(NULL, "\n"); 
     nb_lines = i; 
    } 
    nb_lines++; 

    lines = malloc((nb_lines + 1) * sizeof(char*)); 
    lines[nb_lines] = '\0'; 

    token = strtok(file, "\n"); 
    for(int i = 0; token != NULL; i++) { 
     token = strtok(NULL, "\n"); 
     int nb_char = 0; 
     for(int j = 0; token[j] != '\n'; j++) //This will cause SIGSEGV because strtok don't keep the '\n' at the end 
      nb_char = j; 
     nb_char++; 
     token[nb_char] = '\0'; //This cause SIGSEGV because token's allocation finish at [nb_char-1] 
     lines[i] = malloc(strlen(token) * sizeof(char)); //strlen cause SIGSEGV because I cannot place the '\0' at the end of token 
     printf("%s", token); //SIGSEGV because printf don't find the '\0' 
     lines[i] = token; 
    } 

    for(int i = 0; i < nb_lines; i++) { 
     printf("%s", lines[i]); //SIGSEGV 
    } 

    return nb_lines; 
} 

所以你可以看到什麼,我想要做的想法上面,爲什麼它不工作。

下面你將看到一個其他的嘗試我做了,但我被困在同一點:

int count_subtrings(char* string, char* separator) { 
    int nb_lines = 0; 
    char *token = strtok(string, separator); 
    for(int i = 0; token != NULL; i++) { 
     token = strtok(NULL, separator); 
     nb_lines = i; 
    } 
    return nb_lines + 1; 
} 

char** split_string(char* string, char* separator) { 
    char **sub_strings = malloc((count_subtrings(string, separator) + 1) * sizeof(char*)); 
    for(int i = 0; string[i] != EOF; i++) { 
     //How to get the string[i] lenght to malloc them ? 
    } 
} 

我的文件是相當大的和線可以過,所以我不想對malloc的其他表的大小爲(strlen(file) + 1) * sizeof(char),以確保每行不會SIGSEGV,我也發現這個解決方案很髒,如果你們有其他想法,我會非常高興。

(對不起,我英文錯誤,我不是真的好)

+1

的[從而在C文件中讀取長行處理內存]可能的複製(http://stackoverflow.com/questions/43779687/handle-memory-while-reading-long-lines-from-a -file-in-c) – Badda

+0

你可以使用動態鏈表類型的數據結構。 –

+0

檢出realloc –

回答

0

您與strtok方法有兩個缺點:一是strtok修改字符串,所以只能通過原始字符串一次。其次,它跳過空行,因爲它將trelines延伸爲單個令牌分隔符。(我不知道你是否擔心這個問題。)

您可以通過字符串。分配內存爲您的線陣列,並進行第二次掃描,在那裏你處裂開換行的String:我分配比換行符兩個行指針

char **splitlines(char *msg) 
{ 
    char **line; 
    char *prev = msg; 
    char *p = msg; 

    size_t count = 0; 
    size_t n; 

    while (*p) { 
     if (*p== '\n') count++; 
     p++; 
    } 

    line = malloc((count + 2) * sizeof(*line)); 
    if (line == NULL) return NULL; 

    p = msg; 
    n = 0; 
    while (*p) { 
     if (*p == '\n') { 
      line[n++] = prev; 
      *p = '\0'; 
      prev = p + 1; 
     } 

     p++; 
    } 

    if (*prev) line[n++] = prev; 
    line[n++] = NULL; 

    return line; 
} 

數:一爲的情況下,最後一行沒有按」 t以換行符結束,另一個以末尾放置NULL哨兵,以便您知道yourarray結束的位置。 (你可以,當然,通過指針返回實際的行數到size_t。)

+0

首先,感謝您的幫助,您的算法非常乾淨,我非常喜歡。我有幾個問題,爲什麼你只使用'size_t'變量來通過數組? 「通過指向size_t的指針返回實際行數」是什麼意思,這是如何工作的?如果'* p'爲'0',爲什麼第二個'while(* p)'斷裂?在'line [n ++] = prev;'中,'n'在指令'line [n] = prev;'後面加1。並且是'malloc()'我在我所有的代碼中真的應該關心的失敗嗎? –

+0

(1)'size_t'是一個無符號整數類型;標準庫也將它用於不能爲負數的東西,比如'strlen'返回的值。如果你喜歡,你可以使用'int'。 (2)使函數f(char * s,size_t * pn)'返回前說'if(pn)* pn = n;'(我犯了一個錯誤 - 返回之前的行應該是'lin [n] = NULL',不增加。) –

+0

(3)'line [n ++] = x'是一個典型的C語言。請記住,'n'項的數組有從0到'n-1'的有效索引。項目'n'是緊接在有效範圍之後的項目。追加到數組時,分配給該字段並增加計數。 (4)是的,你應該。在當前代碼中,調用函數應檢查返回的指針是否爲空。快速解決方案分配失敗時,您也可以中止程序。 –

0

以下建議代碼:

  1. 完全編譯
  2. (堆大小的範圍內)沒有按不關心輸入文件的大小
  3. echo是文件行的結果數組,雙倍行距,只是爲了顯示它的工作。爲單倍行距,以printf()

更換puts()和現在的代碼

#include <stdio.h> // getline(), perror(), fopen(), fclose() 
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free() 


int main(void) 
{ 
    FILE *fp = fopen("untitled1.c", "r"); 
    if(!fp) 
    { 
     perror("fopen for reading untitled1.c failed"); 
     exit(EXIT_FAILURE); 
    } 

    // implied else, fopen successful 

    char **lines = NULL; 
    size_t availableLines = 0; 
    size_t usedLines = 0; 

    char *line = NULL; 
    size_t lineLen = 0; 
    while(-1 != getline(&line, &lineLen, fp)) 
    { 
     if(usedLines >= availableLines) 
     { 
      availableLines = (availableLines)? availableLines*2 : 1; 
      char **temp = realloc(lines, sizeof(char*) * availableLines); 
      if(!temp) 
      { 
       perror("realloc failed"); 
       free(lines); 
       fclose(fp); 
       exit(EXIT_FAILURE); 
      } 

      // implied else realloc successful 

      lines = temp; 
     } 

     lines[ usedLines ] = line; 
     usedLines++; 
     line = NULL; 
     lineLen = 0; 
    } 

    fclose(fp); 

    for(size_t i = 0; i<usedLines; i++) 
    { 
     puts(lines[i]); 
    } 

    free(lines); 
} 

鑑於上述代碼是在一個文件名爲:untitled1.c下面是輸出。

#include <stdio.h> // getline(), perror(), fopen(), fclose() 

#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free() 





int main(void) 

{ 

    FILE *fp = fopen("untitled1.c", "r"); 

    if(!fp) 

    { 

     perror("fopen for reading untitled1.c failed"); 

     exit(EXIT_FAILURE); 

    } 



    // implied else, fopen successful 



    char **lines = NULL; 

    size_t availableLines = 0; 

    size_t usedLines = 0; 



    char *line = NULL; 

    size_t lineLen = 0; 

    while(-1 != getline(&line, &lineLen, fp)) 

    { 

     if(usedLines >= availableLines) 

     { 

      availableLines = (availableLines)? availableLines*2 : 1; 

      char **temp = realloc(lines, sizeof(char*) * availableLines); 

      if(!temp) 

      { 

       perror("realloc failed"); 

       free(lines); 

       fclose(fp); 

       exit(EXIT_FAILURE); 

      } 



      // implied else realloc successful 



      lines = temp; 

     } 



     lines[ usedLines ] = line; 

     usedLines++; 

     line = NULL; 

     lineLen = 0; 

    } 



    fclose(fp); 



    for(size_t i = 0; i<usedLines; i++) 

    { 

     puts(lines[i]); 

    } 



    free(lines); 

}