【问题标题】:Do i need to use 2D arrays for an array of strings in C?我是否需要对 C 中的字符串数组使用 2D 数组?
【发布时间】:2019-01-18 15:58:35
【问题描述】:

我希望我的程序从文本文件中读取 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.

【问题讨论】:

  • @Quentin 我真的不相信这个问题是关于 feof 的。
  • @nm_tp 最后一行错误肯定是。
  • 其实,真的。这就是让他发布问题的原因。但是这段代码需要更多的纠正。
  • char **不是二维数组。它根本不是一个数组。

标签: c arrays pointers malloc


【解决方案1】:

你的例子是错误的。在执行fscanf(file,"%s",&amp;word[i]); 行时,第三个参数应该是函数将写入读取数据的地址。在您的情况下,word[i] 是数组的 i-th 元素,&amp;word[i] 是它的地址。因此,单词将与word[i] 的第一个字符一起存储。您的代码只打印一些东西,因为您立即打印它。此外,您不会因为纯粹的机会而遇到段错误。 如果要将字符串读入缓冲区,首先需要为缓冲区分配空间。 通过使用char **,您可以通过首先为指针数组分配足够的空间,然后为每个指针分配足够的空间来保存字符串的地址,从而将其制成二维数组。

我已经为你重写了你的程序:

#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;
}

我还建议不要在这里使用全局变量。

编辑: 正如 cmets 所建议的,此代码不是最佳解决方案。首先,所有的字可能不适合 100 字节的缓冲区。为了缓解这个问题,分配一个固定大小的大缓冲区,将每个字读入其中,然后为 wordList[i] 分配相应数量的字节(不要忘记终止的空字节)并从固定大小的缓冲区复制数据进入wordList[i]

此外,代码缺少一些错误检查。例如,文件可能存在但为空,在这种情况下fscanf(file,"%d",&amp;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;
}

【讨论】:

  • 首先,感谢您的回答,我会以同样的方式编写并试一试。其次,为什么我们专门将每个字符串的最大大小定义为 100 字节?够了吗 ?做malloc(max_string_length * sizeof(char))会不会错?
  • 最好不要将固定大小的字符串分配到 mainpopulateWordsArray 以读取本地 var 中的字符串,然后用 strdup 将其放入向量中
  • @SteliosPapamichail sizeof(char) 按标准等于 1。
  • @bruno 我同意,但这个答案的目的是重组他的代码是正确的,而不是最好的。它仍然检查。单词可能超过 100 个字节,等等。
  • @nm_tp 这个词可以长于 100 的事实是不预先分配的一个原因 ;-) 可能你可以在你的答案中添加第二个实现来解释为什么它是一个更好的实现?只是我的 2 美分
猜你喜欢
  • 2016-09-25
  • 1970-01-01
  • 1970-01-01
  • 2011-02-24
  • 2017-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多