Как сохранить динамическую строку в структуре **, содержащей указатель на char *

В настоящее время я работаю над проблемой сохранения данных из входного файла в структуру следующего формата:

typedef struct school_{
  char *name;
  char *state;
}School;

Я читаю из входного файла в формате:

name1, state1
name2, state2

И я хотел бы динамически хранить данные для каждой школы в структуре с помощью указателей, поскольку длина имени неизвестна. k - количество строк в файле. Пока вот что у меня есть:

void input_schools(FILE *IN, School **Sch, int k) { 
  int i, j;
  char ch;

  for (i=0; i<k; i++)
{ fscanf(IN, "%c", &ch);
  Sch[i].name = (char *)malloc(sizeof (char));

  j = 0;
  Sch[i].name[j] = ch;

  while(ch != '-') {
    fscanf(IN, "%c", &ch);
    j++;
    Sch[i].name = (char *) realloc(Sch[i].name, sizeof(char)*(j+1));
    Sch[i].name[j] = ch;
  }

}
Sch[i].name[j-1] = '\0';

Однако я получаю ошибку seg, которая, как я предполагаю, связана с тем, как я пытаюсь сохранить "ch" при написании "Sch [i] .name [j]". Я также пробовал Sch [i] -> name [ j] и не увенчались успехом. Буду признателен за любую помощь в знании правильного способа написания адреса для хранения данных?

Я вызываю функцию, используя: input_schools (school_info, TOP100, school_size); где информация о школе - это входной файл Школа * TOP100 [school_size]; входит в топ100 а school_size - количество строк в файле

School **Sch, поэтому Sch[i] является указателем - Sch[i].name должен выдать ошибку компилятора. Покажите, как вы вызываете функцию.
Johnny Mopp 13.11.2018 20:40

Кроме того, c-строки должны оканчиваться \0, чего вы не делаете.

Johnny Mopp 13.11.2018 20:43

Попробуйте дать вашим переменным осмысленные имена, особенно в качестве аргументов. Что такое k?

tadman 13.11.2018 20:43

Подсказка: strdup вместо ручного копирования строк.

tadman 13.11.2018 20:44

трепло попробуйте лучшие имена и используйте лучший отступ и форматирование. Наверное, установите линтер, он проверяет ошибки форматирования и компиляции по мере ввода. Кроме того, есть несколько хороших соглашений об именах. например, вместо k я бы написал a_number_of_lines. a_ сообщает мне, что переменная является аргументом функции, а оставшаяся часть сообщает мне, что имеет переменная. Кроме того, если вам не нужно, объявите переменные в цикле. for(int i = 0; i < a_numer_of_lines; i++)

Aravind Voggu 14.11.2018 05:37

@codingstruggles Этот вопрос все еще остается без ответа. Не отвечает ли на него ни один из предоставленных ответов?

Ted Lyngmo 25.05.2019 12:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
242
2

Ответы 2

Ваш файл очень похож по форме на CSV. Посмотрите, можете ли вы использовать какие-либо библиотеки синтаксического анализа csv или код.

Вместо того, чтобы проверять каждый символ, прочитайте всю строку в буфер и используйте strtok. strtok - это функция, используемая для разделения строки по разделителю. a ',' в вашем случае.

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

const char* getfield(char* line, int num)
{
    const char* tok;
    for (tok = strtok(line, ",");
            tok && *tok;
            tok = strtok(NULL, ",\n"))
    {
        if (!--num)
            return tok;
    }
    return NULL;
}

int main()
{
    FILE* stream = fopen("in.md", "r");

    char line[1024];
    while (fgets(line, 1024, stream))
    {
        char* tmp1 = strdup(line);
        char* tmp2 = strdup(line);

        printf("Name is %s\n", getfield(tmp1, 1));
        printf("State is %s\n", getfield(tmp2, 2));
        // NOTE strtok changes the string. Hence two vars. You can try duplicating in the function instead.
        // Note that I'm freeing the data. copying with strdup instead of directly assigning may be wise.
        free(tmp1);
        free(tmp2);
    }
}

К сожалению, мне приходится использовать формат структуры выше. если я использовал strtok для получения каждой строки, есть ли способ сохранить их в структуре, отформатированной, как показано выше?

codingstruggles 13.11.2018 22:13

Есть, но я советую вам воспользоваться ответом Теда Люнгмо. Это намного проще понять и реализовать. Вы можете использовать только часть fscanf, вам не нужно использовать другую часть, но в целом она хороша.

Aravind Voggu 14.11.2018 05:31

Вы можете использовать что-то вроде этого, чтобы прочитать одну школьную запись из предоставленного вами FILE*:

bool School_read(School* s, FILE* in) {

    int scan = fscanf(in, " %m[^,\n], %m[^\n]", &s->name, &s->state);

    // the fscanf format string:
    // <space> = skip leading whitespaces (like a newline from the line before)
    // %m[^,\n] = read a string until, but not including, "," or "\n"  m = allocate space for it
    // , = expect a comma and discard it
    // %m[^\n] = read a string until, but not including, "\n" and allocate space for it

    // just a debug print
    fprintf(stderr, " -- got %d hits, >%s< >%s<\n", scan, s->name, s->state);

    if (scan<2) {
        // not a complete scan, failure
        if (scan==1) {
            // apparently, we got one match, free it
            free(s->name);
            s->name = NULL;
        }
        return false;
    }
    return true;
}

Я не знаю, насколько широко распространена поддержка модификатора «m», который динамически выделяет память для строк. Последние компиляторы gcc и clang все равно поддерживают его.

Вы также можете сделать функции для создания и уничтожения Школы:

School* School_create() {
    School* s = malloc(sizeof(School));
    if (s!=NULL) {
        s->name = NULL;
        s->state = NULL;
    }
    return s;
}

void School_destroy(School** sp) {
    if (sp) {
        School* s = *sp;
        if (s) {
            if (s->state) free(s->state);
            if (s->name) free(s->name);
            free(s);
        }
        *sp = NULL;
    }
}

..и объединить их все:

School* School_create_and_read(FILE* in) {
    School* s = School_create();
    if (s) {
        if (School_read(s, in)==false) {
            School_destroy(&s);
        }
    }
    return s;
}

Итак, в вашей функции, заполняющей массив школ:

void input_schools(FILE* IN, School** Sch, int k) { 
    School* s;
    while( (s=School_create_and_read(IN)) ) {
        // s is a valid School pointer
        // store it in your array 
    }               
}

Ах, это очень мило. Спасибо, что показали нам! Я собираюсь использовать этот способ лучше в следующий раз.

Aravind Voggu 14.11.2018 05:34

Спасибо! Сам я очень редко использую scanf, но есть свои применения :-)

Ted Lyngmo 14.11.2018 07:17

Другие вопросы по теме