你的例子是错误的。在执行fscanf(file,"%s",&word[i]); 行时,第三个参数应该是函数将写入读取数据的地址。在您的情况下,word[i] 是数组的 i-th 元素,&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",&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;
}