В настоящее время я работаю над проблемой сохранения данных из входного файла в структуру следующего формата:
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 - количество строк в файле
Кроме того, c-строки должны оканчиваться \0, чего вы не делаете.
Попробуйте дать вашим переменным осмысленные имена, особенно в качестве аргументов. Что такое k?
Подсказка: strdup вместо ручного копирования строк.
трепло попробуйте лучшие имена и используйте лучший отступ и форматирование. Наверное, установите линтер, он проверяет ошибки форматирования и компиляции по мере ввода. Кроме того, есть несколько хороших соглашений об именах. например, вместо k я бы написал a_number_of_lines. a_ сообщает мне, что переменная является аргументом функции, а оставшаяся часть сообщает мне, что имеет переменная. Кроме того, если вам не нужно, объявите переменные в цикле. for(int i = 0; i < a_numer_of_lines; i++)
@codingstruggles Этот вопрос все еще остается без ответа. Не отвечает ли на него ни один из предоставленных ответов?





Ваш файл очень похож по форме на 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 для получения каждой строки, есть ли способ сохранить их в структуре, отформатированной, как показано выше?
Есть, но я советую вам воспользоваться ответом Теда Люнгмо. Это намного проще понять и реализовать. Вы можете использовать только часть fscanf, вам не нужно использовать другую часть, но в целом она хороша.
Вы можете использовать что-то вроде этого, чтобы прочитать одну школьную запись из предоставленного вами 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
}
}
Ах, это очень мило. Спасибо, что показали нам! Я собираюсь использовать этот способ лучше в следующий раз.
Спасибо! Сам я очень редко использую scanf, но есть свои применения :-)
School **Sch, поэтомуSch[i]является указателем -Sch[i].nameдолжен выдать ошибку компилятора. Покажите, как вы вызываете функцию.