#c #parsing #csv
#c #синтаксический анализ #csv
Вопрос:
Я пытаюсь написать код, который будет анализировать текстовый файл с разделителями табуляции, присваивая каждую строку между табуляциями заданному элементу образца структуры, которую я определил. Во входном файле первая строка будет содержать все идентификаторы классов (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, он выведет регистр класса Case Case . Похоже, это работает до 14, и в этот момент происходит ошибка сегментации всей программы. Я довольно смущен этой проблемой. Я также попытался преобразовать память в образец массива данных, чтобы узнать, была ли это проблема stack vs heap, но, похоже, это тоже не помогает. Большое спасибо за вашу помощь!
sample data[total_columns];
fseek(input, 0, SEEK_SET);
for (int i = 0; i < total_columns; i )
{
fscanf(input, "%[^tnr]tnr", data[i].s_name);
printf("%sn", 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 — уверен, у меня фундаментальное непонимание того, что это на самом деле означает. Мне нужно будет разобраться с этим. Еще раз спасибо за помощь!
Комментарии:
1. Компилируется ли он? Использование
sample
как имени типа, так и имени переменной приведет к путанице, даже если компилятор примет его.2. Последнее поле в строке разделено табуляцией или новой строкой? Если это новая строка, вам нужно это учитывать, не так ли? Вы могли бы рассмотреть
"%[^tn"]%*1[tn]"
, который ищет не табуляции, не новые строки, за которыми следует табуляция или новая строка (*
подавляет присвоение). Вы должны проверить возвращаемое значение изfscanf()
и остановить обработку, если результат не равен 1. Таким образом, вы не получаете никакой информации о символе-разделителе; вы могли бы назначить разделитель также для дополнительной проверки.3. ваши данные состоят из трех строк. Они разделены символом новой строки, а не табуляцией, верно?
4. Хороший момент в том, что тип и имя переменной совпадают. Он компилируется, но я исправил это, чтобы избежать путаницы. Последнее поле в строке заканчивается новой строкой (или в некоторых потенциальных входных файлах — возвратом каретки), и я по глупости изначально думал, что это будет обработано с условием цикла for, но я думаю, что, вероятно, он мог бы просто продолжить чтение через разрыв строки в следующую строкуесли я не укажу останавливаться на n или r, верно? Я попробовал ваше предложение, и, похоже, у меня ошибка сегментации. Я посмотрю, смогу ли я с этим разобраться. Спасибо!
5. BLUEPIXY, да, хотя технически это 3 строки, где первые две строки содержат идентификаторы классов и субъектов, а все последующие строки содержат данные.
Ответ №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, "tn");
while(p){
total_columns;
p = strtok(NULL, "tn");
}
--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, "%*st");//skip first column
for (int i = 0; i < total_columns; i ){
fscanf(input, "%[^tn]t", sample[i].c_name);//n for last column
}
fscanf(input, "%*st");//skip first column
for (int i = 0; i < total_columns; i ){
fscanf(input, "%[^tn]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", amp;sample[i].value[r]);
}
}
fclose(input);
//test print
printf("%sn", sample[0].c_name);
printf("%sn", sample[0].s_name);
for(int i = 0; i < r; i)
printf("%fn", sample[0].value[i]);
printf("n%sn", sample[total_columns-1].c_name);
printf("%sn", sample[total_columns-1].s_name);
for(int i = 0; i < r; i)
printf("%fn", sample[total_columns-1].value[i]);
free(sample);
}
Комментарии:
1. Вау — это очень полезно! Спасибо! Мне определенно придется пройти и проработать часть логики, но это действительно помогает. Один вопрос, который у меня есть заранее, заключается в том, как / почему вы получили 1024 для размера массива строк?
2. @AllieH У него нет определенного основания, достаточно ли его длины. Я думаю, что вы используете
getline
лучше, чемfgets
если бы максимальная длина была неопределенной.3. Потрясающе! Еще раз большое вам спасибо. Это было невероятно полезно.