Непонимание спецификаторов формата C при использовании fscanf()

Итак, я читаю текстовый файл в этом формате:

ABC 51.555 31.555
DEF 23.445 45.345

Я пытаюсь использовать fscanf() для анализа данных, потому что этот файл может увеличиваться или уменьшаться, он должен быть динамическим в том, как он загружается, поэтому я использовал malloc, и я также хочу сохранить его в структуре ниже. Я думаю, что проблема связана с пробелом или даже с неправильным написанием всего спецификатора формата. Вот мой код.

typedef struct data
{
    char name[4];
    char lat[7];
    char lng[7];
}coords;

int main(int argc, char *argv[])
{
    ////////////CREATES FILE POINTER/////////
    FILE* fp;
    ///////////CREATES MALLOC POINTER TO STORE STRUCTS/////////////
    coords* cp;
    //////////OPENS FILE//////////
    fp = fopen(argv[1], "r");
    /////////GET THE TOTAL AMMOUNT OF LINES IN THE FILE/////////
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    rewind(fp);
    //////SKIPS FIRST LINE//////////
    while(fgetc(fp) != (int)'\n')
    {};

    /////////ASSIGNS MEMORY THE SIZE OF THE FILE TO //////////
    cp = malloc(sizeof(coords) * size);

    //////////READS FILE AND STORES DATA///////
    fscanf(fp,"%s[^ ] %s[^ ] %s[^\n]", cp->name, cp->lat, cp->lng);

    printf("%s\n%lf\n%lf\n", cp->name, cp->lat, cp->lng);
    fclose(fp);
    return 0;
}

И да, я знаю, что не включил файлы заголовков, но у меня есть правильные stdlib и stdio.

ОБНОВЛЕНИЕ 1: я пробовал оба ответа, и я получаю это на своем экране:

ABC51.555
0.000000
0.000000

Почему 51,555 не перешли к следующему элементу в структуре? Спасибо

////////////////////////////////////////////////// /////////////ОБНОВЛЕНИЕ 2//////////////////////////////////// //////////////////////

Хорошо, я изменил свой код, чтобы сделать следующее.

typedef struct data
{
    char name[4];
    char lat[6];
    char lng[6];
}coords;

int main(int argc, char *argv[])
{
    ////////////CREATES FILE POINTER/////////
    FILE* fp;
    ///////////CREATES MALLOC POINTER TO STORE STRUCTS/////////////
    coords* cp;
    //////////OPENS FILE//////////
    fp = fopen(argv[1], "r");
    /////////GET THE TOTAL SIZE OF THE FILE/////////
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    long lines = -1;
    rewind(fp);
    //////GETS TOTAL AMMOUNT OF LINES/////////
    char c;
    while(c != EOF)
    {
        c = fgetc(fp);
         if(c == '\n')
         {
            lines++;
         }
    }
    rewind(fp);
    ////////////SKIPS FIRST LINE//////////
    while(fgetc(fp) != (int)'\n')
    {};
    /////////ASSIGNS MEMORY THE SIZE OF THE FILE TO //////////
    cp = malloc(sizeof(coords) * size);

    //////////READS FILE AND STORES DATA///////
    printf("Lines of text read: %d\n", lines);
    fscanf(fp,"%s %s %s[^\n]", cp[0].name, cp[0].lat, cp[0].lng);
    printf("%s\n", cp[0].name);


    fclose(fp);
    return 0;
}

Теперь, когда я пытаюсь напечатать cp[0].name; Я получаю всю первую строку без пробела, вот так.

 ABC51.55531.555

Если я получил печать cp[0].lat; Я понял.

 51.55531.555

И когда я печатаю cp[0].lng; Я понял.

 31.555

Что является единственно правильным, я не могу понять это поведение. Почему оно так себя ведет? все сообщения предполагают (как я сначала подумал), что каждый% s в fscanf будет помещать его в свою собственную переменную, а не объединять их. Неважно, использую ли я точечную нотацию или прямое -> это все равно дает тот же результат. Спасибо :)


person Definity    schedule 12.12.2014    source источник
comment
... используйте цикл .. и используйте cp[index].name   -  person BLUEPIXY    schedule 12.12.2014
comment
Примечание: обычные длинные диапазоны от -180 до 180, широта от -90 до 90, предполагаются большие широта[]/долгота[]   -  person chux - Reinstate Monica    schedule 12.12.2014
comment
Я сделал и обновил текст   -  person Definity    schedule 12.12.2014
comment
Это хороший ответ на ваш пост, но код действительно должен измениться typedef struct data { char name[4]; float lat, lng } coords; ... if (fscanf(fp, "%3s%f%f", cp->name, &cp->lat, &cp->lng) == 3) Success();   -  person chux - Reinstate Monica    schedule 12.12.2014


Ответы (2)


Спецификатор формата "%s[^... пытается прочитать строку, разделенную пробелами, за которой следует символ [, а затем символ ^. Поскольку строка всегда будет заканчиваться пробелом, следующим символом всегда будет пробел, который не будет соответствовать [, и ни один из остальных спецификаторов формата не будет соответствовать.

  • ВСЕГДА проверяйте возвращаемое значение fscanf, чтобы убедиться, что вы прочитали все, что вы сделали. Если возвращаемое значение неверно, дайте диагностику.

  • ВСЕГДА используйте ограничения размера поля при чтении в строковые массивы фиксированного размера.

Итак, в вашем случае вы хотите:

if (fscanf(fp, "%3s%6s%6s", cp->name, cp->lat, cp->lng) != 3) {
    fprintf(stderr, "Incorrect data in input file, exiting!\n");
    abort(); }
person Chris Dodd    schedule 12.12.2014
comment
Примечание. OP неправильно использует printf("%s\n%lf\n%lf\n", cp->name, cp->lat, cp->lng); для печати строк. Подозреваемый OP должен использовать 2 float - person chux - Reinstate Monica; 12.12.2014

Я не уверен, что вы хотите использовать разделитель пробелов [^]. fscanf уже по умолчанию анализирует строку по пробелам. Попробуйте это и посмотрите, правильно ли проанализирована строка:

fscanf(fp, "%s %s %s[^\n]", cp->name, cp->lat, cp-lng);  

вывод должен привести к:

cp->name ---- ABC
cp->lat ----- 51.555
cp->lng ----- 31.555
person buydadip    schedule 12.12.2014