Разбирать строки целых чисел в C

Это классическая проблема, но я не могу найти простого решения.

У меня есть входной файл, например:

1 3 9 13 23 25 34 36 38 40 52 54 59 
2 3 9 14 23 26 34 36 39 40 52 55 59 63 67 76 85 86 90 93 99 108 114 
2 4 9 15 23 27 34 36 63 67 76 85 86 90 93 99 108 115 
1 25 34 36 38 41 52 54 59 63 67 76 85 86 90 93 98 107 113 
2 3 9 16 24 28 
2 3 10 14 23 26 34 36 39 41 52 55 59 63 67 76 

Строки с разным количеством целых чисел, разделенные пробелом.

Я хотел бы проанализировать их в массиве и отделить каждую строку маркером, скажем, -1.

Сложность в том, что я должен обрабатывать целые числа и возврат строки.

Вот мой существующий код, он зацикливается на цикле scanf (потому что scanf не может начинаться с заданной позиции).

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {

  if (argc != 4) {
    fprintf(stderr, "Usage: %s <data file> <nb transactions> <nb items>\n", argv[0]);
    return 1;
  }
  FILE * file;
  file = fopen (argv[1],"r");
  if (file==NULL) {
    fprintf(stderr, "Error: can not open %s\n", argv[1]);
    fclose(file);
    return 1;
  }
  int nb_trans = atoi(argv[2]);
  int nb_items = atoi(argv[3]);
  int *bdd = malloc(sizeof(int) * (nb_trans + nb_items));
  char line[1024];
  int i = 0;

  while ( fgets(line, 1024, file) ) {
    int item;
    while ( sscanf (line, "%d ", &item )){
      printf("%s %d %d\n", line, i, item);
      bdd[i++] = item;
    }
    bdd[i++] = -1;
  }

  for ( i = 0; i < nb_trans + nb_items; i++ ) {
    printf("%d ", bdd[i]);
  }
  printf("\n");
}

person Jérôme    schedule 10.06.2010    source источник
comment
Почему бы не использовать многомерный массив?   -  person Luca Matteis    schedule 10.06.2010
comment
Пожалуйста, пожалуйста, прекратите использовать atoi() прямо сейчас. Да прямо сейчас. Измените каждый вызов atoi() на strtol() и произнесите пятнадцать ошибок града, используя бусины системы счисления.   -  person Tim Post♦    schedule 10.06.2010
comment
@ Тим, если ты собираешься дать совет, хотя бы подкрепи его аргументами. Иначе это просто какой-то рандомный Джо в инете вякает :-)   -  person paxdiablo    schedule 14.06.2010


Ответы (4)


У вас есть несколько вариантов, но в целом я бы напал на это следующим образом:

Считайте входной файл как текстовый файл, то есть набор строк, с помощью fgets(). Это будет читаться до тех пор, пока не будет нажат разрыв строки или EOF. Используйте функцию токенизатора строк, которая сканирует каждую считанную строку на наличие пробелов и возвращает подстроку перед пробелом. Теперь у вас есть строковое представление целого числа. Разберите это в фактический int, если хотите, или сохраните саму подстроку в своем массиве. Если вы переключите его на int, вам нужно остерегаться переполнения, если оно станет слишком большим.

person Michael Dorgan    schedule 10.06.2010
comment
Это кажется немного более эффективным, чем мой ответ. Ну что ж, я давно не использовал C. Я должен больше практиковаться с ним. - person JAB; 10.06.2010
comment
Для меня это тоже было давно, но мне пока не нужно было много использовать строковые вещи С++, поэтому все это все еще довольно свежо для меня. Спасибо за комментарий. - person Michael Dorgan; 10.06.2010

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

Затем создайте свой массив.

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

РЕДАКТИРОВАТЬ: ... И это, кажется, довольно близко к тому, что вы уже делаете, исходя из кода, который вы добавили к своему вопросу, пока я печатал свой ответ.

person JAB    schedule 10.06.2010

ОК, я нашел решение, извините за шум, мне нужно поискать дальше...

чтение неизвестного числа целых чисел из стандартного ввода (C)

Вместо моего цикла scanf используйте этот:

  while ( fgets(line, 1024, file) ) {
    int item;
    for (p = line; ; p = e) {
        item = strtol(p, &e, 10);
        if (p == e)
            break;
        bdd[i++] = item;
    }
    bdd[i++] = -1;
  }
person Jérôme    schedule 10.06.2010
comment
Лол, очень похоже на мое предложение :) - person Michael Dorgan; 10.06.2010
comment
Это. Основная информация в этом решении — это функция strtol, которая возвращает вам конечный указатель поиска. - person Jérôme; 10.06.2010
comment
Опять же, остерегайтесь целочисленного переполнения. Я не уверен, как strol справится со слишком большой строкой, чтобы поместиться в длинный. - person Michael Dorgan; 10.06.2010
comment
Хорошо спасибо. В моем случае я уверен, что записей не более целого числа (на самом деле не более 10000), с этой стороны проблем нет - person Jérôme; 11.06.2010

Вот полная программа на C, которая показывает, как это можно сделать. В основном он считывает строки за раз с помощью fgets, а затем использует sscanf для обработки каждого из inetegrs в этой строке.

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

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

int main (void) {
    char line[1000];
    FILE *fIn;
    char *str;
    int val, num;

    // Open input file and process line by line.

    if ((fIn = fopen ("infile.txt", "r")) == NULL) {
        fprintf (stderr, "Cannot open infile.txt, errno = %d\n", errno);
        return 1;
    }

    while (fgets (line, sizeof (line), fIn) != NULL) {
        // Check if line was too long.

        if (line[strlen (line) - 1] != '\n') {
            fprintf (stderr, "Line too long: [%s...]\n", line);
            fclose (fIn);
            return 1;
        }

        // Oyput the line and start processing it.

        printf ("%s   ", line);
        str = line;

        // Skip white space and scan first inetegr.

        while (*str == ' ') str++;

        num = sscanf (str, "%d", &val);

        // Process the integer if it was there.

        while ((num != 0) && (num != EOF)) {
            // Print it out then skip to next.

            printf ("[%d] ", val);
            while ((*str != ' ') && (*str != '\0')) str++;
            while (*str == ' ') str++;
            num = sscanf (str, "%d", &val);
        }

        // -1 for line separator.

        printf ("[%d]\n", -1);
    }

    // Close input file and exit.

    fclose (fIn);

    return 0;
}

И вот вывод, чтобы показать вам, что он работает:

1 3 9 13 23 25 34 36 38 40 52 54 59
   [1] [3] [9] [13] [23] [25] [34] [36] [38] [40] [52] [54] [59] [-1]
2 3 9 14 23 26 34 36 39 40 52 55 59 63 67 76 85 86 90 93 99 108 114
   [2] [3] [9] [14] [23] [26] [34] [36] [39] [40] [52] [55] [59] [63] [67] [76] [85] [86] [90] [93] [99] [108] [114] [-1]
2 4 9 15 23 27 34 36 63 67 76 85 86 90 93 99 108 115
   [2] [4] [9] [15] [23] [27] [34] [36] [63] [67] [76] [85] [86] [90] [93] [99] [108] [115] [-1]
1 25 34 36 38 41 52 54 59 63 67 76 85 86 90 93 98 107 113
   [1] [25] [34] [36] [38] [41] [52] [54] [59] [63] [67] [76] [85] [86] [90] [93] [98] [107] [113] [-1]
2 3 9 16 24 28
   [2] [3] [9] [16] [24] [28] [-1]
2 3 10 14 23 26 34 36 39 41 52 55 59 63 67 76
   [2] [3] [10] [14] [23] [26] [34] [36] [39] [41] [52] [55] [59] [63] [67] [76] [-1]
person paxdiablo    schedule 10.06.2010