我是否需要在C中的字符串数组中使用2D数组?

时间:2019-01-18 15:58:36

标签: c arrays pointers malloc

我希望我的程序从文本文件中读取N个单词并将其保存在数组中。我的问题是,我是否需要一个2D Array例如:char **wordList还是下面示例中的1D Array就足够了?输出是正确的,除了最后一个string之外,您可以看到这很奇怪。另外,我是否为数组分配了足够的内存,为什么最后一个输出字符串输出错误?

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

void populateWordsArray(int);

FILE *file;
char *word;
char **wordList;

/*
* Function populateWordsArray: reads N words from
* the given file and creates an array with them.
* Has one argument: int N - the number of words to read.
*/
void populateWordsArray(int N) {
    int i = 0;
    while(!feof(file) && i < N) {
    fscanf(file,"%s",&word[i]);
    printf("%s\n",&word[i]);
    i++;
    }
}

int main(int argc,char *argv[]) { // argv[1] = op argv[2] = name
    int N = 0;

    file = fopen(argv[2],"r");

    if(file == (FILE *) NULL) { // check if the file opened successfully
        fprintf(stderr,"Cannot open file\n");
    }

    fscanf(file,"%d",&N); // get the N number
    word = malloc(N * sizeof(char));

    populateWordsArray(N);
    // write a switch method for the various ops
    // call the appropriate function for each operation

    free(word);
    fclose(file);
    return 0;
}

输出:

this
is
a
test!
with
files.
new
line,
here.
ere.

文本文件内容:

10 this is a test! with files.
new line, here.

1 个答案:

答案 0 :(得分:2)

您的示例是错误的。当执行行fscanf(file,"%s",&word[i]);时,第三个参数应该是函数将写入读取数据的地址。在您的情况下,word[i]是数组的第i个元素,&word[i]是其地址。因此,单词将与第一个字符一起存储在word[i]中。您的代码仅打印某些内容,因为您立即将其打印出来。另外,您绝不会碰到段错误。 如果要将字符串读入缓冲区,则首先需要为缓冲区分配空间。 通过使用char **,您可以通过首先为指针数组分配足够的空间,然后为每个指针分配足够的空间以将其保存为字符串的地址,来使其成为2D数组。

我已经为您重写了程序:

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

#define MAX_STRING_LENGTH 100

void populateWordsArray(int);

FILE *file;
char **wordList;

void populateWordsArray(int N) 
{
    int i = 0;
    while(i < N && fscanf(file,"%s", wordList[i]) == 1) // fscanf returns the number of successfully read items. If it's not 1, the read failed. You can also check if the return value is not EOF but, in this situation, it's the same.
    {
        printf("%s\n", wordList[i]); // i-th element is an address to a buffer that contains 100 bytes
        i++;
    }
}

int main(int argc,char *argv[]) 
{ 
    int N = 0, i;

    file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.

    if(file == NULL)  // No need to cast NULL into a specific type.
    {
        fprintf(stderr,"Cannot open file\n");
        return 1; // You might want to end the program here, possibly with non-zero return value.
    }

    fscanf(file,"%d",&N);
    wordList = malloc(N * sizeof(char*)); // Allocating space for pointers
    for(i=0; i<N; i++)
    {
        wordList[i] = malloc(MAX_STRING_LENGTH); // Allocating space for individual strings 
    }

    populateWordsArray(N);

    for(i=0; i<N; i++)
    {
        free(wordList[i]);
    }
    free(wordList);
    fclose(file);
    return 0;
}

我也建议不要在此处使用全局变量。

编辑:如注释所示,此代码不是最佳解决方案。首先,所有的单词可能不适合100字节的缓冲区。为缓解此问题,请分配一个较大的固定大小的缓冲区,将每个字读入其中,然后为wordList[i]分配相应的字节数(不要忘记终止的空字节),并从大小缓冲区放入wordList[i]中。

此外,该代码还缺少一些错误检查。例如,文件可能存在但为空,在这种情况下,fscanf(file,"%d",&N);将返回EOF。此外,文件开头的数字可能与后面的行数不对应,或者N可能是负数(代码通过将其指定为int来允许它) 。

EDIT2:正如@bruno所建议的那样,我制作了一个比以前的版本更防弹的版本。我有可能遗漏了一些东西,我有点着急。如果是这样,请在下面告诉我。

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

#define MAX_STRING_LENGTH 512


char line_buffer[MAX_STRING_LENGTH]; // A line of the maximum size that can occur.

char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines);


char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines)
{
    *readLines=0;
    char** wordList;
    // Allocating space for pointers
    wordList = malloc(wantedLines * sizeof(char*));
    if(!wordList)
    {
        fprintf(stderr,"Cannot allocate sufficient space for the pointers.\n");
        exit(EXIT_FAILURE); // You may return NULL here and check it afterwards. The same goes for all the error checking inside this function
    }

    while(*readLines < wantedLines && fscanf(file,"%s", line_buffer) == 1)
    {
        wordList[*readLines] = malloc(strlen(line_buffer)+1);
        if(!wordList[*readLines])
            break;
        if(NULL == (wordList[*readLines]=strdup(line_buffer)))
            break;
        (*readLines)++;
    }

    return wordList;
}

int main(int argc,char *argv[]) 
{ 
    unsigned N = 0, i, M;
    char **wordList;
    FILE *file;


    file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.

    if(file == NULL)  // No need to cast NULL into a specific type.
    {
        fprintf(stderr,"Cannot open file\n");
        return 1; // You might want to end the program here, possibly with non-zero return value.
    }

    if(fscanf(file,"%d",&N) != 1)
    {
        fprintf(stderr,"Cannot read the number of lines. Empty file?\n");
        return 1;
    }


    wordList = populateWordsArray(N, file, &M);

    printf("Printing the read lines:\n");
    for(i=0; i<M; i++)
    {
        printf("%s\n", wordList[i]);
    }

    for(i=0; i<M; i++)
    {
        free(wordList[i]);
    }
    free(wordList);
    fclose(file);
    return 0;
}