Как разобрать файл .csv с кавычками в C

Допустим, строка, которую я пытаюсь разобрать, читается

«Смит, Джон», Данные1, Данные2, Данные3

Я также мог получить строки, которые читаются

Дэйв, Данные1, Данные2, Данные3

поэтому у меня есть оператор if

строка — это строка текста, fgets() взятая из файла, но я думаю, что это работает

С остальным я боролся около часа или около того. Я пытаюсь переформатировать «Смит, Джон», чтобы это был Джон Смит, а затем назначить его recTemp.artist

if (line[0] == '\"') {
    //Read the last name, first name",
    char lastTemp[30] = "";
    char firstTemp[30] = "";
    strcpy(lastTemp , strtok(line, ", "));
    strcpy(firstTemp, strtok(NULL, "\","));
    char * t;
    t = strstr(lastTemp, "\"");
    strcpy(t, " ");
    //Concatenate each string assign to the artist value
    strcat(firstTemp, lastTemp);
    strcpy(recTemp.artist, firstTemp);
}

Я думаю, что ошибка связана с вызовом strstr или вызовом strcpy сразу после него, но я не уверен

Спасибо!


person Louis Pelletier    schedule 01.02.2019    source источник
comment
Какую ошибку вы имеете в виду? Вы забыли сообщить нам, чего вы ожидаете, что получаете и в чем заключается ваша проблема. Как определены ваши переменные и структуры?   -  person Gerhardh    schedule 01.02.2019
comment
strtok(line, ", ") Как вы уже выяснили, первый символ ", начинать надо со второго символа.   -  person Gerhardh    schedule 01.02.2019


Ответы (2)


Чтобы ответить на ваш вопрос:

"Я пытаюсь переформатировать "Смит, Джон", чтобы это был Джон Смит"

Если не использовать регулярные выражения для извлечения цитат из строки, я бы сделал следующее:

#include <iostream>
#include <cstring>
#include <stdio.h>

int main() {
    char line[100] = "\"Smith,John\",Data1,Data2,Data3";
    // fgets(line, 100, stdin);
    char* name = strtok(line, "\"");
    char *substring2 = strtok(NULL, "\"");
    char* LastName = strtok(name, ",");
    char* FirstName = strtok(NULL, ",");

    char result[100];
    strcpy(result, FirstName);
    strcat(result, ",");
    strcat(result, LastName);
    strcat(result, substring2);
    printf("%s",result);

}

который производит вывод:

Джон, Смит, Данные1, Данные2, Данные3

person Duck Dodgers    schedule 01.02.2019

Если вы хотите избежать замены line на strtok, вы можете просто использовать арифметику указателя, чтобы скопировать либо "first last" в recTemp.artist, либо скопировать "name" в случае, когда в первом поле нет кавычек. Это просто еще один подход, который вы можете использовать, чтобы избежать изменения исходной строки. (и здоровое упражнение в использовании указателей)

В случае, когда присутствуют кавычки, вы можете установить указатель (p) на line + 1 и использовать strstr, чтобы найти подстроку "\",", устанавливающую конечный указатель (endptr) на закрывающую кавычку. Затем вы можете вызвать strchar на p с помощью ',', чтобы найти запятую между last, first и установить другой указатель для перехода к началу имени (firstp). Если у вас есть firstp, указывающий на начало имени, вы можете просто memcpy имя до recTemp.artist, добавить space, а затем скопировать фамилию, завершая нулем после этого.

В случае отсутствия кавычек вам нужно только использовать strchr, чтобы найти разделитель полей ',' и вызвать memcpy, а затем нулевое завершение.

Краткий пример:

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

typedef struct {
    char artist[64];
} rec_t;

int main (void) {
#ifndef NOQUOTE    
    char line[] = "\"Smith, John\",Data1,Data2,Data3";
#else
    char line[] = "Dave,Data1,Data2,Data3";
#endif
    rec_t recTemp;

    if (*line == '\"') {    /* if double-quotes are present */
        char *p = line + 1, *endptr, *sep;    /* ptr, endptr & sep */
        if (!(endptr = strstr (p, "\","))) {  /* find close quote, validate */
            fputs ("error: invalid line format.\n", stderr);
            /* handle error as needed, e.g. */
            return 1;
        }

        if ((sep = strchr (p, ','))) {        /* locate ',' in last, first */
            char *firstp = sep + 1;           /* set firstp to next char */
            while (*firstp && *firstp == ' ') /* skip any leading spaces */
                firstp++;
            memcpy (recTemp.artist, firstp, endptr - firstp); /* copy first */
            endptr = recTemp.artist + (endptr-firstp);  /* set endptr after */
            *endptr++ = ' ';                  /* add a space */
            memcpy (endptr, p, sep - p);      /* copy last */
            *(endptr + (sep - p)) = 0;        /* nul-terminate */
        }
    }
    else {  /* otherwise - name without quotes */
        char *sep = strchr (line, ',');       /* find field seperator */
        if (!sep) {
            fputs ("error: invalid line format.\n", stderr);
            /* handle error as needed, e.g. */
            return 1;
        }
        memcpy (recTemp.artist, line, (sep - line));  /* copy name */
        *(recTemp.artist + (sep - line)) = 0; /* nul-terminate */
    }

    printf ("recTemp.artist: '%s'\n", recTemp.artist);
}

Пример использования/вывода

$ ./bin/rectmp
recTemp.artist: 'John Smith'

Случай имени без кавычек, скомпилированный с -DNOQUOTE:

$ ./bin/rectmpnq
recTemp.artist: 'Dave'

Независимо от того, используете ли вы strtok или проходите несколько указателей вниз по line, все в порядке. Если вы хотите сохранить line без изменений, то либо сделайте копию, прежде чем работать с копией с помощью strtok, либо просто используйте арифметику указателя. Вы можете создать дамп сборки, сгенерированной обоими методами, чтобы увидеть, обеспечивает ли ваш компилятор преимущество оптимизации между методами. Разница будет незначительной по большому счету.

person David C. Rankin    schedule 01.02.2019