Как запросить ввод пользователя, не зная заранее длины? В С - PullRequest
3 голосов
/ 01 октября 2019

Я хочу, чтобы пользовательский ввод в виде {{a, b}, {c, d}}, одной строки, представлял матрицу. Размеры матрицы известны. Используя getchar (), я не могу лечить случаи, когда целые числа больше 9, без невероятно длинного обхода. Используя scanf (), он будет переходить на новую строку после каждого использования этой функции. Как правильно запросить ввод в этом формате? Мне кажется, что должен быть какой-то более простой способ сделать это, чем getchar ().

Как упомянуто ранее, я попытался использовать цикл getchar (), который работал для однозначных целых чисел, но длинацикл становится неправильным, если учитывать несколько цифр. Затем я попробовал приведенный ниже код, но он тоже не работает.

int i=0;
int j=0;
for (int k=0;k<loopSize;k++){
     scanf("%d%c",&element,&c);
         matrix[i][j]=element;
         j++;
         if (j%columns==0){
            i++;
            j=0;
         }
}

Как правильно запросить ввод? Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 01 октября 2019

Вы можете использовать getchar(), как показано ниже

int val = 0;
int number = 0;
int dimLevel = 2;
int state = 0; // 

while ((val = getchar()) != '\n){

    if (val == '{' && state <= dimLevel){
       state++;
    }
    else if (val == '}' && state > 0){
       state--;
    }
    else if (val == ',' && state == dimLevel){
       matrix[i][j] = number;
       number = 0;

       if (j%columns==0){
          i++;
          j=0;
       }
    }
    else if (isdigit(val) && state == dimLevel){
       number = number*10 + (val-'0');
    }
    else if (val == '-' && state == dimLevel){
       number *= -1;
    }
}

if (number && state == 0)
   matrix[i][j] = number; //last number
0 голосов
/ 10 октября 2019

В этом ответе я подумал, что кто-то сказал: «Размеры матрицы неизвестны» вместо «Размеры матрицы известны». Поскольку токены состоят из отдельных символов и цифр, и они всегда следуют одному и тому же шаблону, это можно записать более компактно с помощью scanf из stdio.h.

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

int main(void) {
    int matrix[4];
    char rbrace;
    int success = EXIT_FAILURE;

    if(scanf(" { { %i , %i } , { %i , %i } %c", &matrix[0], &matrix[1],
        &matrix[2], &matrix[3], &rbrace) != 5 || rbrace != '}')
        { errno = EILSEQ; goto catch; }

    printf("( %4d %4d )\n( %4d %4d )\n",
        matrix[0], matrix[1], matrix[2], matrix[3]);

    success = EXIT_SUCCESS;
    goto finally;

catch:
    perror("Something went wrong");

finally:
    return success;
}

. пробел в строке формата: мы хотим, чтобы он пропускал пробел,

Директива, состоящая из одного или нескольких символов пробела, выполняется путем чтения ввода до тех пор, пока не будет считано больше допустимого ввода или допервый байт, который не является символом пробела, который остается непрочитанным.

rbrace используется, чтобы сказать, что у нас есть трейлинг }}. Когда он совпадает с 4-мя целыми числами, я не думаю, что есть способ определить, совпадают ли концевые константы.

Успех литеральных совпадений и подавленных присваиваний определяется только напрямую через преобразование% nСпецификация.

Предостережение, однако, в документации говорится, что %i ожидает ту же последовательность, что и с strtol с 0 в качестве базы, но не совсем ясно, что произойдет, еслиномер вне диапазона. На моем компьютере

{{326345645675372456345576,6435},{6,0}}
(   -1 6435 )
(    6    0 )
0 голосов
/ 02 октября 2019

В общем, анализ текста довольно сложен. Заданная вами грамматика довольно проста, но более сложные грамматики действительно выигрывают от lexing и синтаксического анализа. Вместо того чтобы писать это вручную, в примере я использовал re2c . re2c принимает в качестве входных данных файл C с /*!re2c комментариями и превращает их в быстрый лексер.

x-macro позволяет распечатывать символы и является супер-полезно для отладки, но не требуется в противном случае.

Ccre:

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

/* X-Macro. */

#define PARAM(A) A
#define STRINGISE(A) #A

#define SYMBOL(X) X(END), X(LBRACK), X(RBRACK), X(NUMBER), X(COMMA)

enum Symbol { SYMBOL(PARAM) };
static const char *const symbols[] = { SYMBOL(STRINGISE) };

/* Once the input is read, re2c will lex it; this stores the cursors. */
static struct { const char *from, *to; } scanner;

/*!re2c
re2c:yyfill:enable   = 0;
re2c:define:YYCTYPE  = char;
re2c:define:YYCURSOR = scanner.to;
eof = "\x00"; // If you git re2c 2, there's a simpler new way.
whitespace = [ \t\v\f\n\r];
digit = [0-9];
number = "-"? digit+; */

static enum Symbol next(void) {
reset:
    scanner.from = scanner.to;
/*!re2c
    * { return errno = EILSEQ, END; }
    eof { return END; }
    whitespace+ { goto reset; }
    "{" { return LBRACK; }
    "}" { return RBRACK; }
    number { return NUMBER; }
    "," { return COMMA; }
*/
}

/* Define `CharArray`, a vector of characters -- dynamic string in which we
 will store the entire input. */
#define ARRAY_NAME Char
#define ARRAY_TYPE char
#include "BasicArray.h"

struct Token {
    enum Symbol symbol;
    const char *from;
    int length;
};

#define ARRAY_NAME Token
#define ARRAY_TYPE struct Token
#include "BasicArray.h"

/** Fills the `token` with `symbol` and the most recent scanner values. Returns
 whether it worked. */
static int init_token(struct Token *const token, const enum Symbol symbol) {
    assert(token && symbol && scanner.from && scanner.from < scanner.to);
    if(scanner.from + INT_MAX < scanner.to) return errno = EILSEQ, 0;
    token->symbol = symbol;
    token->from = scanner.from;
    token->length = (int)(scanner.to - scanner.from);
    return 1;
}

#define ARRAY_NAME Int
#define ARRAY_TYPE int
#include "BasicArray.h"

struct Matrix {
    struct IntArray ints;
    size_t cols;
};

/** Returns true if the `tokens` are a valid matrix, and puts it in `matrix`
 which must be empty. It doesn't allow empty matrices. */
static int parse(const struct TokenArray *const tokens,
    struct Matrix *const matrix) {
    size_t set_col = 0, col = 0;
    int is_set_col = 0;
    long input;
    int *output;
    const struct Token *t = 0;
    assert(tokens && matrix && !IntArraySize(&matrix->ints));
    errno = 0;

    /* Left outside. */
    t = TokenArrayNext(tokens, t);
    if(!t || t->symbol != LBRACK) return errno = EILSEQ, 0;
    goto left_middle;

left_middle:
    t = TokenArrayNext(tokens, t);
    if(!t || t->symbol != LBRACK) return errno = EILSEQ, 0;
    col = 0;
    goto inside_number;

inside_number:
    t = TokenArrayNext(tokens, t);
    if(!t || t->symbol != NUMBER) return errno = EILSEQ, 0;
    input = strtol(t->from, 0, 0);
    if(errno) return 0;
    if(input < INT_MIN || input > INT_MAX) return errno = ERANGE, 0;
    if(!(output = IntArrayNew(&matrix->ints))) return 0;
    *output = input;
    col++;
    goto end_number;

end_number:
    if(!(t = TokenArrayNext(tokens, t))) return errno = EILSEQ, 0;
    if(t->symbol == RBRACK) {
        if(is_set_col) {
            if(set_col != col) return errno = EILSEQ, 0;
        } else {
            set_col = col;
            is_set_col = 1;
        }
        goto finished_middle;
    } else if(t->symbol == COMMA) {
        goto inside_number;
    } else {
        return errno = EILSEQ, 0;
    }

finished_middle:
    if(!(t = TokenArrayNext(tokens, t))) return errno = EILSEQ, 0;
    if(t->symbol == RBRACK) {
        goto end_matrix;
    } else if(t->symbol == COMMA) {
        goto left_middle;
    } else {
        return errno = EILSEQ, 0;
    }

end_matrix:
    if((t = TokenArrayNext(tokens, t))) return 0;
    assert(is_set_col);
    matrix->cols = set_col;
    return 1;
}    


int main(void) {
    struct TokenArray tokens = ARRAY_ZERO;
    struct CharArray buffer = ARRAY_ZERO;
    struct Matrix matrix = { ARRAY_ZERO, 0 };
    enum Symbol symbol;
    const size_t granularity = 1024;
    size_t nread, i, n;
    const int *ints;
    int success = EXIT_SUCCESS;

    /* Read all contents from `stdin` at once. */
    do {
        char *read_here;
        if(!(read_here = CharArrayBuffer(&buffer, granularity))
            || (nread = fread(read_here, 1, granularity, stdin), ferror(stdin))
            || !CharArrayExpand(&buffer, nread)) goto catch;
    } while(nread == granularity);

    /* Embed '\0' on the end for simple lexing. */
    {
        char *zero = CharArrayNew(&buffer);
        if(!zero) goto catch;
        *zero = '\0';
    }

    /* We use simplified sentinel method of detecting EOF, no embedded '\0'. */
    {
        const char *const b = CharArrayGet(&buffer);
        const size_t len_to_zero = (size_t)(strchr(b, '\0') - b);
        if(len_to_zero != CharArraySize(&buffer) - 1) {
            errno = EILSEQ;
            fprintf(stderr, "Embedded '\\0' at byte %lu/%lu.\n",
                (unsigned long)len_to_zero,
                (unsigned long)CharArraySize(&buffer) - 1);
            goto catch;
        }
    }

    /* Point the `scanner` to the `buffer`. */
    scanner.to = CharArrayGet(&buffer);

    /* Scan all input. */
    while((symbol = next())) {
        struct Token *const token = TokenArrayNew(&tokens);
        if(!token || !init_token(token, symbol)) goto catch;
        printf("%.*s -> %s\n", token->length, token->from,
            symbols[token->symbol]);
    }
    /* `errno` will be set if a syntax error occurs. */
    if(errno) goto catch;

    /* Parse input if it is a valid matrix. */
    if(!parse(&tokens, &matrix)) goto catch;

    /* Print the matrix. */
    n = IntArraySize(&matrix.ints);
    ints = IntArrayGet(&matrix.ints);
    printf("matrix columns %lu.\n", (unsigned long)matrix.cols);
    for(i = 0; i < n; i++)
        printf("%4d%s", ints[i],
        (i % matrix.cols) == (matrix.cols - 1) ? "\n" : ", ");
    printf("\n");

    goto finally;

catch:
    perror("Something went wrong");
    success = EXIT_FAILURE;

finally:
    IntArray_(&matrix.ints);
    TokenArray_(&tokens);
    CharArray_(&buffer);
    return success;
}

Не совсем уместно на ваш вопрос, но если кто-то хочет попробовать мой код как написано, ему нужно BasicArray.h. Можно было бы поместить максимальные массивы фиксированного размера, но проект, над которым я работаю, имеет динамические массивы переменного размера, поэтому я использовал это.

#include <stdlib.h> /* realloc free */
#include <assert.h> /* assert */
#include <errno.h>  /* errno */

/* Check defines. */
#ifndef ARRAY_NAME /* <-- error */
#error Generic ARRAY_NAME undefined.
#endif /* error --> */
#ifndef ARRAY_TYPE /* <-- error */
#error Generic ARRAY_TYPE undefined.
#endif /* --> */

/* Generics using the preprocessor;
 \url{ http://stackoverflow.com/questions/16522341/pseudo-generics-in-c }. */
#ifdef CAT
#undef CAT
#endif
#ifdef CAT_
#undef CAT_
#endif
#ifdef PCAT
#undef PCAT
#endif
#ifdef PCAT_
#undef PCAT_
#endif
#ifdef T
#undef T
#endif
#ifdef T_
#undef T_
#endif
#ifdef PT_
#undef PT_
#endif
#define CAT_(x, y) x ## y
#define CAT(x, y) CAT_(x, y)
#define PCAT_(x, y) x ## _ ## y
#define PCAT(x, y) PCAT_(x, y)
#define T_(thing) CAT(ARRAY_NAME, thing)
#define PT_(thing) PCAT(array, PCAT(ARRAY_NAME, thing))

typedef ARRAY_TYPE PT_(Type);
#define T PT_(Type)

struct T_(Array);
struct T_(Array) {
    T *data;
    /* Fibonacci; data -> (c0 < c1 || c0 == c1 == max_size). */
    size_t capacity, next_capacity;
    /* !data -> !size, data -> size <= capacity */
    size_t size;
};

/* `{0}` is `C99`. */
#ifndef ARRAY_ZERO /* <-- !zero */
#define ARRAY_ZERO { 0, 0, 0, 0 }
#endif /* !zero --> */

static int PT_(reserve)(struct T_(Array) *const a,
    const size_t min_capacity, T **const update_ptr) {
    size_t c0, c1;
    T *data;
    const size_t max_size = (size_t)-1 / sizeof(T *);
    assert(a);
    if(!a->data) {
        if(!min_capacity) return 1;
        c0 = 8;
        c1 = 13;
    } else {
        if(min_capacity <= a->capacity) return 1;
        c0 = a->capacity;
        c1 = a->next_capacity;
    }
    if(min_capacity > max_size) return errno = ERANGE, 0;
    assert(c0 < c1);
    /* Fibonacci: c0 ^= c1, c1 ^= c0, c0 ^= c1, c1 += c0; */
    while(c0 < min_capacity) {
        size_t temp = c0 + c1; c0 = c1; c1 = temp;
        if(c1 > max_size || c1 < c0) c1 = max_size;
    }
    if(!(data = realloc(a->data, c0 * sizeof *a->data))) return 0;
    if(update_ptr && a->data != data)
        *update_ptr = data + (*update_ptr - a->data);
    a->data = data;
    a->capacity = c0;
    a->next_capacity = c1;
    return 1;
}

static void PT_(array)(struct T_(Array) *const a) {
    assert(a);
    a->data          = 0;
    a->capacity      = 0;
    a->next_capacity = 0;
    a->size          = 0;
}

static void T_(Array_)(struct T_(Array) *const a) {
    if(!a) return;
    free(a->data);
    PT_(array)(a);
}

static void T_(Array)(struct T_(Array) *const a) {
    if(!a) return;
    PT_(array)(a);
}

static size_t T_(ArraySize)(const struct T_(Array) *const a) {
    if(!a) return 0;
    return a->size;
}

static T *T_(ArrayGet)(const struct T_(Array) *const a) {
    return a ? a->data : 0;
}

static T *T_(ArrayNext)(const struct T_(Array) *const a, const T *const here) {
    const T *data;
    size_t idx;
    if(!a) return 0;
    if(!here) {
        data = a->data;
        idx = 0;
    } else {
        data = here + 1;
        idx = (size_t)(data - a->data);
    }
    return idx < a->size ? (T *)data : 0;
}

static T *PT_(new)(struct T_(Array) *const a, T **const update_ptr) {
    assert(a);
    if(!PT_(reserve)(a, a->size + 1, update_ptr)) return 0;
    return a->data + a->size++;
}

static T *T_(ArrayNew)(struct T_(Array) *const a) {
    if(!a) return 0;
    return PT_(new)(a, 0);
}

static T *T_(ArrayBuffer)(struct T_(Array) *const a, const size_t buffer) {
    if(!a || !buffer || !PT_(reserve)(a, a->size + buffer, 0)) return 0;
    return a->data + a->size;
}

static int T_(ArrayExpand)(struct T_(Array) *const a, const size_t add) {
    if(!a) return 0;
    if(add > a->capacity || a->size > a->capacity - add)
        return errno = ERANGE, 0;
    a->size += add;
    return 1;
}

/* Prototype. */
static void PT_(unused_coda)(void);
/** This silences unused function warnings from the pre-processor, but allows
 optimisation, (hopefully.)
 \url{ http://stackoverflow.com/questions/43841780/silencing-unused-static-function-warnings-for-a-section-of-code } */
static void PT_(unused_set)(void) {
    T_(Array_)(0);
    T_(Array)(0);
    T_(ArraySize)(0);
    T_(ArrayGet)(0);
    T_(ArrayNext)(0, 0);
    T_(ArrayNew)(0);
    T_(ArrayBuffer)(0, 0);
    T_(ArrayExpand)(0, 0);
    PT_(unused_coda)();
}
/** {clang}'s pre-processor is not fooled if you have one function. */
static void PT_(unused_coda)(void) { PT_(unused_set)(); }

/* Un-define all macros. */
#undef ARRAY_NAME
#undef ARRAY_TYPE
#undef CAT
#undef CAT_
#undef PCAT
#undef PCAT_
#undef T
#undef T_
#undef PT_

Один компилирует это:

re2c -W -o C.c C.c.re
gcc -Wall -Wextra -O3 -pedantic -ansi C.c

Введите это:

{{77,88,}}
^D

Получите:

{ -> LBRACK
{ -> LBRACK
77 -> NUMBER
, -> COMMA
88 -> NUMBER
, -> COMMA
} -> RBRACK
} -> RBRACK
Something went wrong: Illegal byte sequence

Введите это:

     {{9, -8},  {-7,  4},  {2, 0}  }
^D

Получите:

{ -> LBRACK
{ -> LBRACK
9 -> NUMBER
, -> COMMA
-8 -> NUMBER
} -> RBRACK
, -> COMMA
{ -> LBRACK
-7 -> NUMBER
, -> COMMA
4 -> NUMBER
} -> RBRACK
, -> COMMA
{ -> LBRACK
2 -> NUMBER
, -> COMMA
0 -> NUMBER
} -> RBRACK
} -> RBRACK
matrix columns 2.
   9,   -8
  -7,    4
   2,    0

Оглядываясь назад,это, вероятно, излишне для этой проблемы, но хорошо масштабируется. Кроме того, при комплексном тестировании обычно проще тестировать лексер отдельно от парсера, который, как правило, использует гораздо меньше символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...