Как использовать sscanf для чтения строки csv с запятой продолжения - PullRequest
0 голосов
/ 27 апреля 2019

Я пытаюсь разобрать строку CSV-файла следующим образом:

47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,34,67,57,43,27,49
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85

формат: id, firstName, фамилия, middlename, hw1, hw2, hw3, midterm1, midterm2, final.И, возможно, не было второго имени, как я могу использовать sscanf для правильного чтения второй строки.

Сначала я использую getline, чтобы построчно читать файл, а затем помещаю строку в sscanf для анализа, чтобы получить правильное значение.соответствующее значение, и я читаю char за char, чтобы увидеть, есть ли в строке непрерывная запятая, если есть две запятые, используйте sscanf без второго имени

    char *line = NULL;
    size_t len = 1000;
     while(getline(&line, &len, stdin)!= EOF)
     {

        int idNum, final;
        char* firstName = malloc(100);
        char* lastName =malloc(100);
        char* middleName =malloc(100);
        int hw1, hw2, hw3;
        int m1, m2;
        Student * student = malloc(sizeof(Student));
        student->m_scores = malloc(sizeof(Midterms));

        int i;
        int counter =0;

        for (i=0; i< strlen(line); i++){
            if(line[i] == ',' && line[i+1] == ',')
                {counter++;}
        }
        printf("counter: %d\n", counter);

        if (counter == 1)
        {   
            sscanf(line,"%d ,%[^,],%[^,],%0[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName,middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);

        }
        else{

            sscanf(line,"%d ,%[^,],%[^,], %[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);

        }

вот мой код для чтения строки без второго имени

sscanf(line,"%d ,%[^,],%[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, &hw1, &hw2, &hw3, &m1, &m2, &final);

вот мой код для чтения строки с отчеством

sscanf(line,"%d ,%[^,],%[^,], %[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);

вот мои фактические результаты

47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,41,76,47,42,70,83
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85

здесь ожидаемый результат

47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,34,67,57,43,27,49
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85

Ответы [ 2 ]

0 голосов
/ 27 апреля 2019

Вам все равно нужно проверить значение, возвращаемое sscanf(). Ваш трюк с использованием счетчика строк по своей природе хрупок и не может быть расширен для работы с десятками студентов, не говоря уже о сотнях или тысячах или более. Что вам нужно сделать, это больше похоже на:

int rc = sscanf(line, "%d , %[^,], %[^,], %[^,],%d ,%d ,%d ,%d ,%d ,%d",
                &idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);
if (rc == 10)
{
    /* All present and correct */
}
else if (rc == 3)
{
    /* Problem at middle name — presumably it is missing */
    rc = sscanf(line, "%d , %[^,], %[^,],,%d ,%d ,%d ,%d ,%d ,%d",
           &idNum, firstName, lastName, &hw1, &hw2, &hw3, &m1, &m2, &final);
    if (rc != 9)
    {
        /* Misformatted still — there is an irresolvable problem with this line */
    }
    else
    {
        /* All except middle name present and correct */
        middlename[0] = '\0';
    }
}
else
{
    /* Misformatted — there is an irresolvable problem with this line */
}
/* Process information here — unless you did it at the 'present and correct' lines */

При возникновении неразрешимой проблемы вы можете сообщить об ошибке, заключив в кавычки полную строку (что нельзя сделать, если вы не используете getline() плюс sscanf() - поэтому рекомендуется использовать комбинацию).

Когда нет неразрешимой проблемы, вы можете приступить к копированию данных в недавно распределенные структуры. Вы можете решить, что будете обрабатывать больше кодов возврата, применяя соответствующие исправления к данным. Помните, что sscanf() прекращает анализ при первой ошибке.

Все строковые входы должны быть ограничены - поскольку вы, кажется, выделяете 100 байтов, вы должны использовать %99[^,]. Вы можете подумать, могут ли имена содержать пробелы - и если нет, вы можете использовать %99[, ] или даже %99[^, \t\n] или аналогичные (и вы можете подумать о добавлении пробела между набором сканирования и следующей запятой, чтобы любые пробелы в конце после того, как имя не отсканировано (например, пространство перед набором сканирования пропускает любой пробел перед именем. Возможно, это позволяет обрабатывать плохо отформатированные данные. Это не является плохой вещью автоматически. (Это закон Постеля или * 1015). * Принцип устойчивости : Этот принцип также известен как закон Постела, после того, как Джон Постел, который писал в ранней спецификации TCP: реализации TCP должны следовать общему принципу устойчивости: будьте консервативны в том, что вы делаете, будьте либерал в том, что вы принимаете от других. )

Вы также можете разработать схему, основанную на strcspn(), для идентификации символов между запятыми. Вы обработаете каждое поле в строку, а затем при необходимости преобразуете строку в число (и проверяете числа: отрицательные оценки, оценки свыше 100 и т. Д., Вероятно, недопустимы). Это самая гибкая схема. Он также может защитить вас от целочисленных переполнений, которых нет у sscanf().

0 голосов
/ 27 апреля 2019

Вы должны не делать middleName = NULL; перед вызовом sscanf(). Вы должны предоставить действительный указатель на память для хранения отчества. Если строка имеет пустое отчество, она заполнится пустой строкой.

Нет необходимости сначала читать строку за символом. Просто позвольте sscanf() сделать свое дело.

...