Разбор текстового файла с разделителями табуляцией

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

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

Я могу предположить, что, например, для идентификаторов классов мне, вероятно, следует использовать fscanf в цикле for, добавляя каждый идентификатор в поле класса данного образца, но я теряюсь в фактической реализации. Основываясь на одном посте, который я видел, я подумал, что могу сделать что-то вроде использования %[^\t]\t в fscanf для чтения в массив всего, что не является вкладкой, но я не думаю, что это совершенно правильно.

Любые предложения будут ценны.

#define LENGTH 30
#define MAX_OBS 80000

typedef struct
{
    char c_name[LENGTH];
    char s_name[LENGTH];
    double value[MAX_OBS];
}
sample;

// I've already calculated the number of columns in the file
sample sample[total_columns];
for (int i = 0; i < total_columns; i++)
   {
      fscanf(input, "%[^\t]\t", sample[i].s_name);
   }

Изменить: я пробовал несколько различных вариантов кода ниже ("%[^\t\n\r]\t\n\r" или "%[^\t\n\r]%*1[\ t\n\r]", или " %[^\t\n\r]"), и все они, похоже, в целом работают, за исключением того, что в зависимости от размера, который я выделяю данным, и от того, как долго я итерирую , в какой-то момент это дает ошибку сегментации. Приведенный ниже код немедленно выдает ошибку сегментации, но если я произвольно изменю total_columns в обоих местах на 3, он напечатает Class Case Case. Кажется, это работает до 14, после чего происходит сбой сегментации всей программы. Я довольно запутался в этом вопросе. Я также попытался распределить память по массиву данных образца, чтобы увидеть, была ли это проблема стека или кучи, но это, похоже, тоже не помогает. Большое спасибо за Вашу помощь!

sample data[total_columns];
fseek(input, 0, SEEK_SET);
for (int i = 0; i < total_columns; i++)
{
    fscanf(input, "%[^\t\n\r]\t\n\r", data[i].s_name);
    printf("%s\n", data[i].s_name);
}

Пример входного файла будет выглядеть так:

Class   Case    Case    Case    Case    Case    Case    Case    Case    Case    Case    Case    Case    Case    Case    Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control
Subject G038    G144    G135    G161    G116    G165    G133    G069    G002    G059    G039    G026    G125    G149    G108    G121    G060    G140    G127    G113    G023    G147    G011    G019    G148    G132    G010    G142    G020    G021
Data1   0.000741628 0.00308607  0.000267431 0.001418697 0.001237904 0.000761145 0.0008281   0.002426075 0.000236698 0.004924871 0.000722752 0.003758006 0.000104813 0.000986619 0.000121803 0.000666854 0   0.000171394 0.000877993 0.002717391 0.001336501 0.000812089 0.001448743 5.28E-05    0.001944298 0.000292529 0.000469631 0.001674047 0.000651526 0.000336615
Data2   0.102002396 0.108035127 0.015052531 0.079923731 0.020643362 0.086480609 0.017907667 0.016279315 0.076263965 0.034876124 0.187481931 0.090615572 0.037460171 0.143326961 0.029628502 0.049487575 0.020175439 0.122975405 0.019754837 0.006702899 0.014033264 0.040024363 0.076610375 0.069287599 0.098896479 0.011813681 0.293331246 0.037558052 0.303052867 0.137591517
Data2   0.218495065 0.242891829 0.23747851  0.101306336 0.309040188 0.237477347 0.293837554 0.34351816  0.217572429 0.168651691 0.179387106 0.166516699 0.099970652 0.181003474 0.076126675 0.10244981  0.449561404 0.139257863 0.127579104 0.355797101 0.354544105 0.262855651 0.10167146  0.186068602 0.316763006 0.187466247 0.05701315  0.123825467 0.064780343 0.069847682
Data4   0.141137543 0.090948286 0.102502388 0.013063365 0.162060849 0.166292135 0.070215996 0.063535037 0.333743609 0.131011609 0.140936687 0.150108506 0.07812762  0.230704405 0.069792935 0.120770743 0.164473684 0.448110378 0.42599534  0.074094203 0.096525097 0.157661185 0.036737518 0.213931398 0.091119285 0.438073807 0.224921728 0.187034237 0.06611442  0.086005218
Data5   0.003594044 0.003948354 0.008137536 0.001327901 0.002161974 0.003552012 0.002760334 0.001898667 0.001420186 0.003165988 0.001011853 0.001217382 0.000314439 0.004254794 0.000213155 0.003650147 0   0.002742309 0.002633978 0   0.002524503 0.002146234 0.001751465 0.006543536 0.003941146 0.00049505  0.00435191  0.001944054 0.001303053 0.004207692
Data6   0.000285242 2.27E-05    0   1.13E-05    0.0002964   3.62E-05    0.000138017 0.000210963 0.000662753 0   0   0   0   4.11E-05    0   0   0   0   0.000101307 0   0   0   0   5.28E-05    0.00152391  0   0   0   0   0
Data7   0.002624223 0.001134584 0.00095511  0.000419934 0.000401011 0.001739761 0.00272583  0.002566717 0.000520735 0.002311674 0.006287944 0   6.29E-05    0.000143882 3.05E-05    0.000491366 0   0   3.38E-05    0   0.001782002 0.000957104 0.002594763 0.000527704 0.000105097 0.001192619 3.13E-05    0   0.000744602 0.000252461
Data8   0.392777683 0.383875286 0.451499522 0.684663315 0.387394299 0.357992026 0.488406597 0.423473155 0.27267563  0.47454646  0.331020526 0.484041709 0.735955056 0.338841956 0.781699147 0.625403622 0.313596491 0.270545891 0.379259109 0.498913043 0.372438372 0.446271644 0.606698813 0.305593668 0.360535996 0.29889739  0.328710081 0.521222594 0.419924299 0.584111756

Редактировать: я, кажется, исправил это, изменив определение MAX_OBS - почти уверен, что у меня есть фундаментальное непонимание того, что это на самом деле означает. Я должен изучить это. Спасибо еще раз за помощь!


person Allie H    schedule 03.10.2016    source источник
comment
Он компилируется? Использование sample как имени типа, так и имени переменной может привести к путанице, даже если компилятор это примет.   -  person Jonathan Leffler    schedule 03.10.2016
comment
Последнее поле в строке ограничено табуляцией или новой строкой? Если это новая строка, вам нужно принять это во внимание, не так ли? Вы можете рассмотреть "%[^\t\n"]%*1[\t\n]", который ищет не вкладки, не новые строки, за которыми следует табуляция или новая строка (* подавляет присваивание). Вы должны проверить возвращаемое значение из fscanf() и остановить обработку, если результат не равен 1. Таким образом, вы не получаете никакой информации о символе-разделителе; вы также можете назначить разделитель для дополнительной проверки.   -  person Jonathan Leffler    schedule 03.10.2016
comment
ваши данные имеют три строки. Они разделены новой строкой, а не табуляцией, верно?   -  person BLUEPIXY    schedule 03.10.2016
comment
Хорошо, что тип и имя переменной совпадают. Он компилируется, но я исправил это, чтобы избежать путаницы. Последнее поле в строке заканчивается новой строкой (или в некоторых потенциальных входных файлах возвратом каретки), и я по глупости изначально думал, что это будет обрабатываться с условием цикла for, но я думаю, что это может быть просто продолжайте читать через разрыв строки в следующую строку, если я не укажу, чтобы остановиться на \n или \r, верно? Я попробовал ваше предложение, и, похоже, у меня возникла ошибка сегментации. Я посмотрю, смогу ли я это решить. Спасибо!   -  person Allie H    schedule 03.10.2016
comment
BLUEPIXY, да, хотя технически это 3++ строк, где первые две строки содержат идентификаторы класса и предмета, а все последующие строки содержат данные.   -  person Allie H    schedule 03.10.2016
comment
Покажите нам пример входного файла.   -  person BLUEPIXY    schedule 03.10.2016
comment
Я добавил пример входного файла.   -  person Allie H    schedule 03.10.2016


Ответы (1)


попробуй это:

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

#define LENGTH 30
#define MAX_OBS 80000

typedef struct{
    char c_name[LENGTH];
    char s_name[LENGTH];
    double value[MAX_OBS];
} Sample;//Duplication of type and variable names should be avoided. pointed out by Jonathan Leffler.

int main(void){
    char line[1024];
    FILE *input = fopen("data.txt", "r");

    fgets(line, sizeof(line), input);

    int total_columns = 0;
    char *p = strtok(line, "\t\n");

    while(p){
        ++total_columns;
        p = strtok(NULL, "\t\n");
    }
    --total_columns;//first column is field name
    rewind(input);
 //*******************************************************************************
    Sample *sample = malloc(total_columns * sizeof(*sample));//To allocate in the stack is large. So allocate by malloc.

    fscanf(input, "%*s\t");//skip first column
    for (int i = 0; i < total_columns; i++){
        fscanf(input, "%[^\t\n]\t", sample[i].c_name);//\n for last column
    }
    fscanf(input, "%*s\t");//skip first column
    for (int i = 0; i < total_columns; i++){
        fscanf(input, "%[^\t\n]\t", sample[i].s_name);
    }
    int r;
    for(r = 0; r < MAX_OBS; ++r){
        if(EOF==fscanf(input, "%*s")) break;
        for (int i = 0; i < total_columns; i++){
            fscanf(input, "%lf", &sample[i].value[r]);
        }
    }
    fclose(input);

    //test print
    printf("%s\n", sample[0].c_name);
    printf("%s\n", sample[0].s_name);
    for(int i = 0; i < r; ++i)
        printf("%f\n", sample[0].value[i]);
    printf("\n%s\n", sample[total_columns-1].c_name);
    printf("%s\n", sample[total_columns-1].s_name);
    for(int i = 0; i < r; ++i)
        printf("%f\n", sample[total_columns-1].value[i]);
    free(sample);
}
person BLUEPIXY    schedule 03.10.2016
comment
Вау - это супер полезно! Спасибо! Мне определенно придется пройти и проработать некоторую логику, но это действительно помогает. Один вопрос, который у меня есть заранее, заключается в том, как/почему вы пришли к 1024 для размера линейного массива? - person Allie H; 03.10.2016
comment
@AllieH Нет определенного основания, достаточно ли его длины. Я думаю, что вы используете getline лучше, чем fgets Если максимальная длина неизвестна. - person BLUEPIXY; 03.10.2016
comment
Потрясающий! Еще раз большое спасибо. Это было невероятно полезно. - person Allie H; 03.10.2016