Я делаю программу, чтобы показать ненулевой элемент в матрице n * n

Бывший. первый. Я введу строку и столбец

Затем введите матрицу, которую я хочу проанализировать.

Проблема здесь!

Например, если я хочу иметь матрицу 4 * 4

Моему типу ввода понравится:

input:    
    0 0 4 0
    0 4 0 5
    1 7 5 8
    1 0 5 4

И я хочу вывести "ошибку", когда кто-то вводит 4 * 4 вот так (это 2 * 12)

input:
    1 0 4 2 0 4 1 4
    5 0 1 4 8 0 0 4

Или что (даже не квадрат)

input:
   1 2 4 2 5 4 1 7 8 1
   4 5 7 0 0 5


  #include <stdio.h>
  void main(){

  int i;
  int j;
  int row;
  int col;

  printf("Please Enter Row & Column : ");
  scanf("%d%d",&row,&col);

  int a[row][col];

  printf("Please Enter Matrix: \n");
  for (i=0; i<row; i++){
       for(int j=0; j<col;j++){

       scanf("%d",&a[i][j]);
       }
  }

  int size =0;
      for (i=0;i<row;i++)
          for (j = 0; j <col; j++)
              if(a[i][j] != 0)
                  size++;

  int b[size][3];


      int k = 0;
      for (int i = 0; i < row; i++)
          for (int j = 0; j <col; j++)
              if (a[i][j] != 0)
              {
                  b[k][0] = i;
                  b[k][1] = j;
                  b[k][2] = a[i][j];
                  k++;
              }
  for (int i=0; i<size; i++)
      {
          for (int j=0; j<3; j++)
              printf("%d ", b[i][j]);

          printf("\n");
      }
      return 0;

  }
1
昌翰余 11 Окт 2019 в 21:20
3
Используйте fgets. Затем проанализируйте строки.
 – 
Eugene Sh.
11 Окт 2019 в 21:21
2
Что бы вы ни делали, не используйте scanf.
 – 
Steve Summit
11 Окт 2019 в 21:43

2 ответа

Создайте буфер и прочитайте всю строку с помощью fgets. Затем используйте strtok и strtol для анализа целых чисел.

fgets: https://en.cppreference.com/w/c/ io / fgets

strtok: https://en.cppreference.com/w/ c / строка / байт / strtok

strtol: https://en.cppreference.com/w/ c / строка / байт / strtol

А вот пример кода:

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

#define MAXLEN 65535
#define M_SIZE 4

int main() {
    char buf[MAXLEN];
    int matrix[M_SIZE][M_SIZE];

    for (int i = 0; i < M_SIZE; i++) {
        // read the whole line
        fgets(buf, MAXLEN, stdin);

        char* num_str = strtok(buf, " ");  // get first integer
        for (int j = 0; j < M_SIZE; j++) {
            if (num_str == NULL) {
                printf("less than 4 integers inputted in one line\n");
                return -1;
            }
            matrix[i][j] = strtol(num_str, NULL, 10);
            num_str = strtok(NULL, " ");  // get next integer
        }

        if (num_str != NULL) {  // if the next integer exists
            printf("more than 4 integes inputted in one line");  // this is the error you want
            return -1;
        }
    }

    return 0;
}

Другой способ - использовать sscanf согласно комментарию

sscanf работает так же, как scanf, за исключением того, что он принимает строку в качестве входных данных. Он возвращает аргумент, который он проанализировал. Ввод '% n' в строку формата поместит количество прочитанных символов в назначенный int и не увеличит счетчик, возвращаемый функцией. https://en.cppreference.com/w/c/io/fscanf

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

#define MAXLEN 65535
#define M_SIZE 4

int main() {
    char buf[MAXLEN];
    int matrix[M_SIZE][M_SIZE];

    for (int i = 0; i < M_SIZE; i++) {
        // read the whole line
        fgets(buf, MAXLEN, stdin);

        int parsed_len;
        int ret = sscanf(buf, "%d %d %d %d %n", &matrix[i][0], &matrix[i][1], &matrix[i][2], &matrix[i][3], &parsed_len);  // parse the line
        if (ret < 4) {
            printf("less than 4 integers inputted in one line\n");
            return -1;
        }
        else if (parsed_len == strlen(buf)) {
            printf("more than 4 integes inputted in one line\n");  // the error you want
            return -1;
        }
    }

    return 0;
}
4
KagurazakaKotori 12 Окт 2019 в 09:45
1
Гораздо проще и проще использовать sscanf для синтаксического анализа строки - просто обязательно проверьте код возврата и используйте %n, чтобы узнать, сколько из строки потребляется каждым вызовом sscanf.
 – 
Chris Dodd
11 Окт 2019 в 23:28
strtok не требуется. Передавая значение в качестве второго аргумента в strtol, вы, по сути, также получаете токенизацию из strtol.
 – 
William Pursell
13 Окт 2019 в 17:32

Вы можете сделать что-то вроде:

/* Read text data into an array. */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>

struct args {
        int rows; /* number of rows in the matrix */
        int cols; /* number of columns in the matrix */
};
void die(const char *fmt, ... )  __attribute__ ((format (printf, 1, 2)));
void usage(const char *name);
void parse_args(int argc, char **argv, struct args *A);


int
main(int argc, char **argv)
{
        struct args A;
        int row=0, col=0; /* indices into the array */

        parse_args(argc, argv, &A);

        int a[A.rows][A.cols];
        char buf[1024];
        char *end;
        while( fgets(buf, sizeof buf, stdin) ) {
                if( row >= A.rows ) {
                        die("Too many lines of input");
                }
                for( end=buf, col=0; col < A.cols && end < buf + sizeof buf; col++ ) {
                        char *s = end;
                        a[row][col] = strtol(end, &end, 10);
                        if(! isspace(*end) || (end == s)) {
                                die("invalid input at line %d, column %ld: '%c'",
                                         row + 1, end - buf + 1, *end);
                        }
                }
                if(col != A.cols) {
                        die("missing inputs in line %d.", row + 1);
                }
                while(isspace(*end)) {
                        end += 1;
                }
                if( *end ) {
                        die("extra data in line %d", row + 1);
                }
                row += 1;
        }
        for( row=0; row < A.rows; row++ ) {
                printf("%d: ", row);
                for(col=0; col < A.cols; col++) {
                        printf("%d  ", a[row][col]);
                }
                printf("\n");
        }
}

void die(const char *fmt, ... )
{
        va_list ap;
        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
        va_end(ap);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
}

void
usage(const char *name)
{
        const char *base = strrchr(name, '/');
        printf("usage: %s rows columns", base ? base + 1 : name);
        puts("\n\nrows and columns must be positive integers");
        exit(EXIT_SUCCESS);
}

void
parse_args(int argc, char **argv, struct args *A)
{
        char *end;
        int ch;
        if( argc < 3 ) {
                usage(argv[0]);
        }
        while( (ch = getopt(argc, argv, ":bf:")) != -1 ) {
                switch( ch ) {
                case 'h': usage(argv[0]);
                default: die("Unexpected option: %s (-h for usage)", argv[optind-1]);
                }
        }
        argc -= optind;
        argv += optind;
        A->rows = strtol(argv[0], &end, 10);
        if( A->rows < 0 || *end ) {
                die("invalid parameter: %s (-h for usage)", argv[0]);
        }
        A->cols = strtol(argv[1], &end, 10);
        if( A->cols < 0 || *end ) {
                die("invalid parameter: %s (-h for usage)", argv[1]);
        }
}

Но я бы сказал, что вы не должны. Этот формат ввода действительно не подходит. Если у вас есть такие неструктурированные данные, вы должны настоять на том, чтобы производитель использовал лучший формат. Если вы не можете изменить производителя, вам следует использовать другой язык для изменения данных, чтобы сделать их более подходящими. Например, ваш код C может быть просто:

/* Read integer data into an array. */
#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
        unsigned rows, cols;

        if( argc != 3 ||
                ( rows = strtol(argv[1], NULL, 10)) == 0 ||
                ( cols = strtol(argv[2], NULL, 10)) == 0
        ) {
                fputs("invalid parameters\n", stderr);
                return EXIT_FAILURE;
        }

        int a[rows][cols];

        if(fread(a, sizeof **a, rows* cols, stdin) != rows * cols) {
                fputs("Unexpected EOF\n", stderr);
                return EXIT_FAILURE;
        }
}

И учитывая текстовый файл ввода, вы можете использовать его с:

perl -ane 'print pack("i*", @F)' input | ./a.out ${N?} ${M?}

Обратите внимание, что теперь это будет работать с входным файлом, в котором все данные находятся в одной строке, или если данные структурированы неправильно. (например, одна строка с 5 записями и другая строка с 3 записями может быть прочитана как матрица 2x4 или 4x2). Независимо от того, является ли это проблемой или особенностью, смотрящий видит. Если это функция, ничего делать не нужно. Если это проблема, поместите проверку ошибок в фильтр (например, perl), а не в код C. Если вы действительно хотите проверить формат, вы можете ввести символ терминала в поток данных в конце каждой строки (например, perl -lane 'print pack("i*", @F)' input, чтобы поместить новую строку после каждой строки), а затем читать по одной строке за раз, затем выполните getchar, чтобы убедиться, что существует '\n', а затем в конце выполните getchar и убедитесь, что вы получили EOF ... но я действительно не думаю, что это стоит того.

Обратите внимание, что в обоих этих примерах я указал размер матрицы в качестве аргументов командной строки. Это кажется уместным, поскольку ваш код кажется предназначенным для интерактивного использования. Если вы хотите прочитать параметры из потока данных, это довольно тривиальное изменение. Просто прочтите из потока вместо того, чтобы разбирать командную строку.

0
William Pursell 16 Окт 2019 в 22:45
Отличный код, из любопытства, почему вы включаете libgen.h?
 – 
KamilCuk
13 Окт 2019 в 17:43
Для базового имени
 – 
William Pursell
13 Окт 2019 в 18:56
Но обратите внимание, что я отключил вызов basename. так что можно добавить квалификатор const.
 – 
William Pursell
14 Окт 2019 в 19:06