Нужно ли использовать 2D-массивы для массива строк в C?

То же самое произошло со мной, а не с помощью кнопки ввода типа ввода = кнопка, как описано ниже -

<input type="button" id="submit" value="submit" name="submit" onclick="showHint(URL.value)"></input>

она наверняка сработает

-1
задан Stelios Papamichail 18 January 2019 в 15:58
поделиться

1 ответ

Ваш пример неверен. При выполнении строки fscanf(file,"%s",&word[i]); третьим аргументом должен быть адрес, куда функция запишет прочитанные данные. В вашем случае word[i] - это i -й элемент массива, а &word[i] - его адрес. Таким образом, слово будет сохранено с первым символом в word[i]. Ваш код печатает только что-то, потому что вы печатаете это немедленно. Кроме того, вы не получаете segfault по чистой случайности. Если вы хотите прочитать строку в буфер, вам сначала нужно выделить место для буфера. Используя 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;
}

Я также не рекомендовал бы использовать здесь глобальные переменные.

РЕДАКТИРОВАТЬ: Как следует из комментариев, этот код не является лучшим решением. Во-первых, все слова могут не помещаться в 100-байтовый буфер. Чтобы решить эту проблему, выделите большой буфер фиксированного размера, прочитайте каждое слово в нем, затем выделите соответствующее количество байтов для wordList[i] (не забудьте завершающий нулевой байт) и скопируйте данные из буфера фиксированного размера в wordList[i].

Кроме того, в коде отсутствуют некоторые проверки ошибок. Например, файл может существовать, но он пуст, и в этом случае fscanf(file,"%d",&N); вернет EOF. Кроме того, число в начале файла может не соответствовать количеству строк, которые следуют, или N может быть отрицательным числом (код допускает это, указав его как int).

РЕДАКТИРОВАТЬ 2: Как предложил @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;
}
0
ответ дан nm_tp 18 January 2019 в 15:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: