Мой входной файл выглядит так:

1,12,5,14,0
4,6,2,3,24
1,2,3,4,5

Каждая строка имеет одинаковую длину, но не указано, сколько строк и какой длины. (Максимальный размер 20 * 20).

Мне нужно прочитать их в массиве 20 * 20, чтобы, если длина строки всего 2 числа, остальная часть массива была пустой.

Как это сделать, пропуская символы , и начиная со следующей строки массива, когда в файле появляется новая строка?

Вот моя попытка:

int matrix[20][20];

for (int i=0; i<19; i++){
    for (int j=0; j<19; j++){
        fscanf(be, "%d,", &matrix[i][j]);
    }
    fscanf(be, "\n");
}

Ps: мне разрешено использовать только <stdio.h> и <stdlib.h> .

-1
Kende Détár 27 Ноя 2019 в 14:14
3
Если вам искусственно запрещено проезд по string.h и / или ctype.h, то вас ждет ухабистая дорога. Тот, кто установил это ограничение, не является инженером в реальном мире, поэтому напоминайте себе об этом, путешествуя по шоссе, напрасно произнося его имя на каждой выбоине. Хотя тебе придется хотя бы попробовать. Представьте свою попытку как минимальный воспроизводимый пример неудачного случая и конкретные вопросы по этому поводу, и мы сможем помочь.
 – 
WhozCraig
27 Ноя 2019 в 14:24
2
Где твой код? Попробуйте написать свой собственный код, и тогда вы сможете указать нам на свои проблемы!
 – 
Sir Jo Black
27 Ноя 2019 в 14:31
Это не переполнение домашнего задания. Сначала сделайте попытку и прочтите Как задать вопрос
 – 
klutt
27 Ноя 2019 в 14:34
1
Я бы посоветовал спросить совета у вашего учителя, но я бы прочитал каждую строчку с fgets, а затем прочитал числа с sscanf.
 – 
klutt
27 Ноя 2019 в 14:39
1
Строки в вашем примере имеют разную длину. У них одинаковое количество номеров. Это не то же самое.
 – 
Gerhardh
27 Ноя 2019 в 15:43

5 ответов

У меня была такая идея. Приведенный ниже код считывает строку из файла для каждого цикла цикла и просматривает ее, если это не пустая строка. Разделитель SEP извлекается с помощью функции (реализованной в коде) mystrchr

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

char * mystrchr(const char * s, char x);

char * mystrchr(const char * s, char x) 
{
    while(*s!=0 && *s!=x) s++;

    return ((!*s)?NULL:(char *)s);
}

int main(void)
{
#define FILENAME "x.txt"
#define ROWS 20
#define MAXEL 20
#define BDIM 10240
#define SEP ','

    int x[ROWS][MAXEL],elr[ROWS], ronum=0,elnum,i,j;
    FILE * fptr;
    char buffer[BDIM],* app;

    /* --- Init the array --- */
    for(i=0;i<ROWS; i++)
        for(j=0;j<MAXEL; j++)
            x[i][j]=0;

    /* --- Open the file --- */
    fptr=fopen(FILENAME,"r");
    if (!fptr)
        return 1;

    /* --- Gets data from file --- */
    while(ronum<ROWS && fgets(buffer,BDIM,fptr)) {
        app = buffer;
        /* Takes only non-void lines */
        if (*app && *app!='\n' && *app!='\r') {
            elnum=0;--app;
            /* Scan the line */
            do {
                if (* ++app ) {
                    x[ronum][elnum++]=strtol(app,NULL,0);
                }

                app = mystrchr(app,SEP);
            } while(app && elnum<MAXEL);

            // -- save the number of element per row
            if (elnum) {
                elr[ronum]=elnum;
                ronum++;
            }
        }
    }


    if (fptr)
        fclose(fptr);

    /* --- Prints data --- */
    for(i=0;i<ronum;i++) {
        for(j=0;j<elr[i];j++) {
            printf("%d ",x[i][j]);
        }
        puts("");
    }

    return 0;
}
1
Sir Jo Black 27 Ноя 2019 в 17:15
1
Почему бы просто не запустить с помощью int x[ROWS][MAXEL] = {0}; Также см. обсуждение другого ответа atoi.
 – 
Goodbye SE
27 Ноя 2019 в 16:02
Я модифицировал исходный код. Я использовал strtol вместо atoi. Я начинаю думать, что для решения вопроса можно использовать только strtol ...
 – 
Sir Jo Black
27 Ноя 2019 в 16:29

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

  • если вы видите ',', вы видели число;

  • если вы видите '\n', вы видели число и видели линию.

  • вы пропускаете каждый нецифровой символ;

  • вы помещаете цифры в небольшой временный буфер. как только вы увидели число, вы можете преобразовать его с помощью atoi;

  • если вам не разрешено использовать atoi, вы выполняете number= number*10 + c-'0'; с c цифрой для каждой цифры, которую вы видите.

0
Paul Ogilvie 27 Ноя 2019 в 14:41
Звучит как очень плохой и сложный способ выполнить эту задачу.
 – 
klutt
27 Ноя 2019 в 14:45
Видя, что это домашнее задание, а возможный набор инструментов ограничен, я бы сделал это точно так же. Не могли бы добавить информацию о том, почему вы думаете, что это очень плохо и сложно ?
 – 
domsson
27 Ноя 2019 в 14:48
1
Пожалуйста, не рекомендуйте никому использовать atoi. Это опасная и на 100% лишняя функция. Вместо этого используйте strtol.
 – 
Lundin
27 Ноя 2019 в 14:54
@ Лундин, в чем опасность?
 – 
Paul Ogilvie
27 Ноя 2019 в 14:58
1
Это в точности так, как говорит Лундин. Лучше никогда не изучать эту функцию. Точно так же можно рассуждать с gets. Конечно, сработает, но зачем к этому привыкать?
 – 
klutt
27 Ноя 2019 в 15:31

Из описания не ясно, может ли строка быть пустой.

Предполагая, что все строки непустые, это довольно простая задача scanf(), но важным моментом является никогда не отбрасывать возвращаемое значение функций ввода .

Пара возможных подходов:

  • Сканируйте, используя шаблон %d для первого числа в каждой строке (возвращаемое значение 0 означает, что мы прочитали все строки), а затем ,%d для последующих чисел в этой строке. scanf() вернет 0, если во входном потоке нет ,, и мы перейдем к следующей строке.
  • Сканируйте, используя шаблон %d%c, чтобы записать символ, следующий за числом, в переменную (например, char sep). Когда возвращаемое значение равно 0, мы прочитали все строки и прервали цикл. Когда возвращаемое значение равно 2, мы можем исследовать sep, чтобы увидеть, является ли оно , или \n, и выбрать, следует ли перейти к следующей строке. (Если возвращаемое значение равно 1, то последнее число было в конце файла без последней строки новой строки).

Если некоторые или все строки могут быть пустыми, вы захотите посмотреть, является ли первый символ строки новой строкой (возможно, с getc() и ungetc()). Это будет означать пустую строку.

0
Toby Speight 27 Ноя 2019 в 19:51

На самом деле вам нужно всего лишь <stdio.h>. Вы можете разделить значения, считывая каждую строку в буфер с помощью fgets(). Вы можете проанализировать каждое значение из буфера, используя sscanf, поддерживая смещение от начала буфера, используя спецификатор "%n" для определения количества символов, потребляемых во время каждого преобразования в int. Вы добавляете это число (+1 для учета ',') к вашему смещению для каждого преобразованного значения.

( примечание: в идеале при заданном stdlib.h вы хотели бы использовать strtol для обработки преобразований, поскольку он обеспечивает полную обработку ошибок, чтобы вы могли определить причину сбоя преобразования и обработать сбой. , но без limits.h и errno.h вы не можете определить, произошло ли недостаточное / переполнение во время преобразования (без предоставления ваших собственных значений LONG_MIN/LONG_MAX), поэтому вы ограничены только проверкой того, были ли преобразованы символы - что можно использовать sscanf)

В вашем случае первые константы #define для вашего количества строк, столбцов и размера буфера, например

#include <stdio.h>

#define ROWS 20     /* if you need a constant, #define one (or more) */
#define COLS ROWS
#define MAXC 1024

Теперь объявите переменные массива и буфера, а также строки и столбца, откройте имя файла, указанное в качестве первого аргумента программы, и убедитесь, что оно открыто для чтения (или чтения из stdin по умолчанию, если аргумент не указан):

int main (int argc, char **argv) {

    char buf[MAXC] = "";    /* buffer to hold each line read */
    int arr[ROWS][COLS] = {{0}}, row = 0, col = 0;  /* array & bounds */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

Теперь прочтите каждую строку в файле в buf и объявите свои переменные, которые вы будете использовать для отслеживания offset от начала buf, количества символов n, потребляемых во время каждого конверсия, количество столбцов в строке ncol и проанализированное значение val:

    while (row < ROWS && fgets (buf, MAXC, fp)) {     /* read each line */
        int offset = 0,     /* offset from beginning of line */
            n = 0,          /* number of characters consumed in conversion */
            ncol = 0,       /* number of columns in line */
            val;            /* value from conversion */

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

        while (ncol < COLS && sscanf (buf + offset, "%d%n", &val, &n) == 1) {
            arr[row][ncol++] = val; /* assign val, increment ncol */
            offset += n + 1;        /* update offset + 1 for ',' */
        }

Теперь немного по хозяйству. Если вам нужно убедиться, что каждая строка в массиве имеет одинаковое количество столбцов, установите количество столбцов на основе количества значений, проанализированных в первой строке. Затем для всех других строк сравните с этим значением, чтобы убедиться, что у вас есть равное количество значений для каждой строки:

        if (!col)           /* if number of columns not set */
            col = ncol;     /* set to no. columns in 1st row */
        if (ncol != col) {  /* force all other rows to have same no. cols */
            fputs ("error: unequal number of columns.\n", stderr);
            return 1;
        }

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

        row++;      /* increment row count */
    }

По сути, это все, что вам нужно сделать. Все, что остается, - это закрыть файловый поток (если не выполняется чтение из stdin) и использовать значения в массиве, как вам нравится. Полный пример может быть таким:

#include <stdio.h>

#define ROWS 20     /* if you need a constant, #define one (or more) */
#define COLS ROWS
#define MAXC 1024

int main (int argc, char **argv) {

    char buf[MAXC] = "";    /* buffer to hold each line read */
    int arr[ROWS][COLS] = {{0}}, row = 0, col = 0;  /* array & bounds */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (row < ROWS && fgets (buf, MAXC, fp)) {     /* read each line */
        int offset = 0,     /* offset from beginning of line */
            n = 0,          /* number of characters consumed in conversion */
            ncol = 0,       /* number of columns in line */
            val;            /* value from conversion */
        /* loop over each value in line */
        while (ncol < COLS && sscanf (buf + offset, "%d%n", &val, &n) == 1) {
            arr[row][ncol++] = val; /* assign val, increment ncol */
            offset += n + 1;        /* update offset + 1 for ',' */
        }
        if (!col)           /* if number of columns not set */
            col = ncol;     /* set to no. columns in 1st row */
        if (ncol != col) {  /* force all other rows to have same no. cols */
            fputs ("error: unequal number of columns.\n", stderr);
            return 1;
        }
        row++;      /* increment row count */
    }

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

    for (int i = 0; i < row; i++) {     /* output array */
        for (int j = 0; j < col; j++)
            printf (" %3d", arr[i][j]);
        putchar ('\n');
    }
}

Пример просто выводит значения, хранящиеся в вашем массиве, и завершает работу.

Пример входного файла

$ cat dat/2020max.txt
1,12,5,14,0
4,6,2,3,24
1,2,3,4,5

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

$ ./bin/read2020max dat/2020max.txt
   1  12   5  14   0
   4   6   2   3  24
   1   2   3   4   5

Просмотрите все и дайте мне знать, если у вас возникнут дополнительные вопросы.

0
David C. Rankin 27 Ноя 2019 в 20:49

Предполагая хорошо отформатированные данные, прочтите строку с помощью fgets(), а затем проанализируйте его с помощью strtol()

#define COL_MAX 20
#define ROW_MAX 20
#define CHAR_PER_INTEGER (sizeof(long)*CHAR_BIT/3 + 3)
#define LINE_MAX (ROW_MAX * (CHAR_PER_INTEGER + 1) + 2)

long matrix[ROW_MAX][COL_MAX] = {0};  // better to use named constants than magic numbers

int col = 0; 
int row; 
// for (int i=0; i<19; i++){  Not to 19, but 20
for (row=0; row<ROW_MAX; row++){
  char line[LINE_MAX*2]; // Generous buffer, but not insane size
  if (fgets(line, sizeof line, be) == NULL)) {
    break; // No more lines
  }
  char *s = line;
  for (int j=0; j<COL_MAX; j++){
    char *endptr;
    errno = 0;
    long value = strtol(s, &endptr, 10);
    // no conversion, overflow, unexpected next character
    if (s == endptr || errno || (*endptr != ',' && *endptr != '\n')) {
      fprintf(stderr, "Invalid input <%s>\n", line);
      exit(EXIT_FAILURE);
    }
    matrix[row][j] = value;
    if (*endptr == '\n') {  // no more in this line
      col = j + 1;
      break;
    }
    s = endptr + 1; // advance past the ','
  }
}

// Use row*col portion of the matrix.
0
chux - Reinstate Monica 28 Ноя 2019 в 01:49