печать матрицы - PullRequest
       6

печать матрицы

3 голосов
/ 05 марта 2011

Я пытаюсь реализовать универсальную функцию для печати 2D-данных.Я пришел к выводу:

int mprintf(FILE* f, char* fmt, void** data, size_t cols, size_t rows)

Задача состоит в том, чтобы определить, сколько бит считывать одновременно из data, на основе fmt.

Формат fmt это будет специфичный для stdlib формат для printf() и тому подобное.

У вас есть какие-либо знания об уже существующих функциях из stdlibc (GNU GCC C), которые я мог бы использовать для упрощения этого?*

Я стараюсь избегать необходимости делать все это вручную, потому что я знаю: «Я тупой» (я не хочу вводить глупые ошибки).Таким образом, повторное использование кода будет наиболее свободным от ошибок.

Спасибо

Приложение

Я вижу, что есть /usr/include/printf.h.Не могу ли я использовать какие-либо из этих функций, чтобы сделать это правильно и одновременно облегчить свою работу?

Ответы [ 5 ]

6 голосов
/ 05 марта 2011

Дизайн, предложенный в вопросе:

int mprintf(FILE *f, char *fmt, void **data, size_t cols, size_t rows);

Точки проектирования высокого уровня

  1. Если вы хотите напечатать секцию 4x4 матрицы 8x8, вам нужно знать строкудлина матрицы, а также размер для печати.Или вы можете предпочесть, чтобы это было отдельной функцией.
  2. Предположительно, формат будет определять разделение между элементами матрицы, или вы пробьете пробел между ними, или как?(Если пользователь указывает «% d», все ли числа будут объединены?)
  3. Вы неявно предполагаете, что матрица будет напечатана сама по себе, с выравниванием по левому краю на странице.Как бы вы адаптировали интерфейс для печати матрицы в другом месте?Ведущие места на линии?Текст перед каждой строкой матрицы?Текст после строки матрицы?

Точки проектирования низкого уровня

  1. Строка формата должна быть const char *.

  2. Очевидно, ваш код может делать то, что делает printf(), более или менее.Он смотрит на спецификатор преобразования формата, а затем определяет, какой тип собирать.Ваш код будет немного сложнее, в некоторых отношениях.Вам нужно будет трактовать массив unsigned char иначе, чем массив short и т. Д. C99 предусматривает модификатор hh для signed char или unsigned char (перед спецификаторами формата d, i, o, u, x или X) и модификатор h для short или unsigned short.Вы, вероятно, должны признать это тоже.Аналогично, модификаторы L для long double и l для long и ll для long long должны обрабатываться.Интересно, что printf() не должен иметь дело с float (потому что любое одиночное значение float автоматически повышается до double), но ваш код должен будет это сделать.По аналогии с h и L вы, вероятно, должны использовать H в качестве модификатора для указания массива float.Обратите внимание, что этот случай означает, что вам нужно будет передать функции printf() формат, отличный от указанного пользователем.Вы можете сделать копию предоставленного пользователем формата, опуская 'H' (или использовать точно предоставленный пользователем формат, кроме случаев, когда он содержит 'H'; вы не будете изменять строку формата пользователя - не в последнюю очередь потому, что пересмотренный интерфейсговорит, что это постоянная строка).

  3. В конечном счете, ваш код должен определить размер элементов в массиве.Возможно, вы измените интерфейс для включения этой информации - по аналогии с такими функциями, как bsearch() и qsort() или fread() и fwrite().Или вы можете определить его по спецификатору формата.

  4. Обратите внимание, что хотя GCC допускает арифметику указателей на void *, стандарт C не позволяет.

  5. Вы уверены, что хотите void ** в интерфейсе?Я думаю, что было бы легче понять, если вы передадите адрес начального элемента массива - один уровень указателя.

    short s[3][4];
    float f[2][5];
    char  c[20][30];
    
    mprintf(fp, "%3hd",   &s[0][0],  4,  3);
    mprintf(fp, "%8.4Hf", &f[0][0],  5,  2);
    mprintf(fp, "%hhu",   &c[0][0], 30, 20); 
    

    Изменяет параметр data на void *.Может быть, у меня нет кофеина, но я не понимаю, как заставить работать двойной указатель разумно.

Outline

  • Определить размер элементов и исправитьстрока формата.
  • Для каждой строки
    • Для каждого столбца
    • Найти данные для элемента
    • Вызвать соответствующую функцию, чтобы напечатать ее
    • Напечатайте разделитель, если вам нужно
    • Напечатайте новую строку

Иллюстрация

В этом коде предполагается, что соглашение «0 успешно»,Предполагается, что вы имеете дело с числами, а не с матрицами указателей или строк.

typedef int (*PrintItem)(FILE *fp, const char *format, void *element);

static int printChar(FILE *fp, const char *format, void *element)
{
    char c = *(char *)element;
    return (fprintf(fp, format, c) <= 0) ? -1 : 0;
}

...and a whole lot more like this...

static int printLongDouble(FILE *fp, const char *format, void *element)
{
    long double ld = *(long double *)element;
    return (fprintf(fp, format, ld) <= 0) ? -1 : 0;
}


int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
    char *format = strdup(fmt);
    int rc = 0;
    size_t size;
    PrintItem print;

    if ((rc = print_info(format, &size, &print)) == 0)
    {
        for (size_t i = 0; i < rows; i++)
        {
            for (size_t j = 0; j < cols; j++)
            {
                 void *element = (char *)data + (i * cols + j) * size;
                 if ((rc = print(fp, format, element)) < 0)
                      goto exit_loop;
            }
            fputc('\n', fp);  // Possible error ignored
        }
    }

exit_loop:
    free(fmt);
    return rc;
}

static int print_info(char *fmt, size_t *size, PrintItem *print)
{
    ...analyze format string...
    ...set *size to the correct size...
    ...set *print to the correct printing function...
    ...modify format string if need so be...
    ...return 0 on success, -1 on failure...
}

Рабочий код

Оставлено как упражнение:

  1. Указатели
  2. Строки
  3. size_t
  4. intmax_t
  5. ptrdiff_t

Обратите внимание, что я обычно не буду использоватьоператоры += или *= в той же строке, что и другие назначения;это было удобно для генерации тестовых чисел.

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

/* mprintf() - print a matrix of size cols x rows */
extern int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows);

typedef int (*PrintItem)(FILE *fp, const char *format, void *element);

static int printChar(FILE *fp, const char *format, void *element)
{
    char value = *(char *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printShort(FILE *fp, const char *format, void *element)
{
    short value = *(short *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printInt(FILE *fp, const char *format, void *element)
{
    int value = *(int *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printLong(FILE *fp, const char *format, void *element)
{
    long value = *(long *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printLongLong(FILE *fp, const char *format, void *element)
{
    long long value = *(long long *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printFloat(FILE *fp, const char *format, void *element)
{
    float value = *(float *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printDouble(FILE *fp, const char *format, void *element)
{
    double value = *(double *)element;
    return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}

static int printLongDouble(FILE *fp, const char *format, void *element)
{
    long double valued = *(long double *)element;
    return (fprintf(fp, format, valued) <= 0) ? -1 : 0;
}

/* analyze format string - all arguments can be modified */
static int print_info(char *format, size_t *size, PrintItem *print)
{
    char *fmt = format;
    char c;
    bool scanning_type = false;
    int hcount = 0;
    int lcount = 0;
    int Hcount = 0;
    int Lcount = 0;
    char *Hptr = 0;

    while ((c = *fmt++) != '\0')
    {
        switch (c)
        {
        case '%':
            if (*fmt == '%')
                fmt++;
            else
                scanning_type = true;
            break;

            /* Length modifiers */
        case 'h':
            if (scanning_type)
                hcount++;
            break;
        case 'l':
            if (scanning_type)
                lcount++;
            break;
        case 'L':
            if (scanning_type)
                Lcount++;
            break;
        case 'H':
            if (scanning_type)
            {
                Hptr = fmt - 1;
                Hcount++;
            }
            break;

            /* Integer format specifiers */
        case 'd':
        case 'i':
        case 'o':
        case 'u':
        case 'x':
        case 'X':
            if (scanning_type)
            {
                /* No floating point modifiers */
                if (Hcount > 0 || Lcount > 0)
                    return -1;
                /* Can't be both longer and shorter than int at the same time */
                if (hcount > 0 && lcount > 0)
                    return -1;
                /* Valid modifiers are h, hh, l, ll */
                if (hcount > 2 || lcount > 2)
                    return -1;
                if (hcount == 2)
                {
                    *size = sizeof(char);
                    *print = printChar;
                }
                else if (hcount == 1)
                {
                    *size = sizeof(short);
                    *print = printShort;
                }
                else if (lcount == 2)
                {
                    *size = sizeof(long long);
                    *print = printLongLong;
                }
                else if (lcount == 1)
                {
                    *size = sizeof(long);
                    *print = printLong;
                }
                else
                {
                    *size = sizeof(int);
                    *print = printInt;
                }
                return 0;
            }
            break;

            /* Floating point format specifiers */
        case 'e':
        case 'E':
        case 'f':
        case 'F':
        case 'g':
        case 'G':
        case 'a':
        case 'A':
            if (scanning_type)
            {
                /* No integer modifiers */
                if (lcount > 0 || hcount > 0)
                    return -1;
                /* Can't be both float and long double at once */
                if (Lcount > 0 && Hcount > 0)
                    return -1;
                /* Cannot repeat L or H modifiers */
                if (Lcount > 1 || Hcount > 1)
                    return -1;
                if (Lcount > 0)
                {
                    *size = sizeof(long double);
                    *print = printLongDouble;
                }
                else if (Hcount > 0)
                {
                    /* modify format string, dropping the H */
                    assert(Hptr != 0 && strlen(Hptr+1) > 0);
                    memmove(Hptr, Hptr+1, strlen(Hptr));    // Copy '\0' too!
                    *size = sizeof(float);
                    *print = printFloat;
                }
                else
                {
                    *size = sizeof(double);
                    *print = printDouble;
                }
                return 0;
            }
            break;

        default:
            break;
        }
    }

    return -1;
}

int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
    char *format = strdup(fmt);     // strdup() is not standard C99
    int rc = 0;
    size_t size;
    PrintItem print;

    if ((rc = print_info(format, &size, &print)) == 0)
    {
        for (size_t i = 0; i < rows; i++)
        {
            for (size_t j = 0; j < cols; j++)
            {
                void *element = (char *)data + (i * cols + j) * size;
                if ((rc = print(fp, format, element)) < 0)
                {
                    fputc('\n', fp);    // Or fputs("<<error>>\n");
                    goto exit_loop;
                }
            }
            fputc('\n', fp);  // Possible error ignored
        }
    }

exit_loop:
    free(format);
    return rc;
}

#ifdef TEST
int main(void)
{
    short s[3][4];
    float f[2][5];
    char  c[8][9];
    FILE *fp = stdout;

    int v = 0;
    for (size_t i = 0; i < 3; i++)
    {
        for (size_t j = 0; j < 4; j++)
        {
            s[i][j] = (v += 13) & 0x7FFF;
            printf("s[%zu,%zu] = %hd\n", i, j, s[i][j]);
        }
    }

    v = 0;
    for (size_t i = 0; i < 8; i++)
    {
        for (size_t j = 0; j < 9; j++)
        {
            c[i][j] = (v += 13) & 0x7F;
            printf("c[%zu,%zu] = %hhu\n", i, j, c[i][j]);
        }
    }

    float x = 1.234;
    for (size_t i = 0; i < 2; i++)
    {
        for (size_t j = 0; j < 5; j++)
        {
            f[i][j] = x *= 13.12;
            printf("f[%zu,%zu] = %g\n", i, j, f[i][j]);
        }
    }

    mprintf(fp, " %5hd",      &s[0][0], 4, 3);
    mprintf(fp, "%%(%3hhu) ", &c[0][0], 8, 9);
    mprintf(fp, " %11.4He",   &f[0][0], 5, 2);
    mprintf(fp, " %11.4He",    f,       5, 2);

    return 0;
}
#endif /* TEST */
1 голос
/ 05 марта 2011

Предполагая, что я понял ваши требования, и предполагая, что fmt определяет, как форматировать только один элемент (он может быть расширен, чтобы вы могли не передавать его непосредственно в (f) printf, а описать, как напечатать всю матрицу - который, я думаю, более полезен), вам просто нужно сделать несколько простых манипуляций со строками, чтобы определить тип данных и решить, как привести ваш указатель void ** (смотрите здесь ).

Случаи не будут бесконечными, а ограничены только тем, сколько типов данных вы хотите поддерживать.

После того, как вы навели указатель, сработает простой цикл for, основанный на столбцах и строках.

1 голос
/ 05 марта 2011

Нет средств для универсального применения, все зависит от того, КАК вы хотите, чтобы это отображалось.Тем не менее, следующее является довольно общим, и вы можете адаптировать его, если вам нужно, чтобы оно немного изменилось:

int mprintf(FILE* file, char* fmt, int** data, size_t cols, size_t rows) {
for(int r=0; r<rows; r++) {
    for(int c=0; c<cols; c++) {
        fprintf(file, fmt, data[r][c]); // fmt can be e.g. "%9i"
    }
    printf("\n");
}
}
1 голос
/ 05 марта 2011

Вы не можете определить тип аргумента из строки формата printf.Проблема OP в том, что он хочет печатать как printf, но использовать аргументы указателем как scanf.К сожалению, спецификаторов преобразования printf недостаточно для стороны 'scanf' задачи.Рассмотрим "% f", который printf использует для чисел с плавающей точкой или двойных чисел.Правила преобразования аргументов для функций с переменными значениями означают, что printf всегда видит двойные значения и не заботится о них, но в этом случае scanf нужны "% f" и "% lf", чтобы различать их.ОП также не может пытаться использовать версии спецификаторов scanf, так как это нарушит printf.Ни один из них не является подмножеством другого.

Ваша универсальная функция должна быть указана как минимум как для спецификатора преобразования формата, так и для определения размера элементов для выполнения правильной арифметики указателя.(На самом деле, если вы собираетесь делегировать fprintf и друзьям, размер недостаточен - вам нужно знать тип на сайте вызова fprintf).Я бы, вероятно, передал бы функцию, а не два параметра, чтобы избежать риска несовпадения между ними, таким образом:

#include <stdio.h>

typedef const void *(*Printfunc)(FILE *f, const void *datum);

/* print an integer and advance the pointer */
static const void* print_int(FILE *f, const void *datum)
{
    const int* p = datum;
    fprintf(f, "%d", *p);
    return p + 1;
}

/* print a char and advance the pointer */
static const void* print_char(FILE *f, const void *datum)
{
    const char* p = datum;
    fprintf(f, "%c", *p);
    return p + 1;
}

static void mprint(FILE *f, Printfunc p, const void *data, size_t cols, size_t rows)
{
    const void *next = data;
    int i;
    for (i = 0; i < rows; ++i) {
        int j;
        for (j = 0; j < cols; ++j) {
            next = p(f, next);
            putc(' ', f);
        }
        putc('\n', f);
    }
}

int main()
{
    int imatrix[3][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 } };
    char cmatrix[2][2] = { { 'a', 'b' }, { 'c', 'd' } }; 

    mprint(stdout, print_int, imatrix, 2, 3);
    mprint(stdout, print_char, cmatrix, 2, 2);

    return 0;
}
0 голосов
/ 05 марта 2011

Я думаю, что путем типизации void ** данных, основанных на * fmt, можно добиться цели.Я не уверен, правильно ли я понимаю ваш вопрос.Поскольку вы можете, вы можете поместить оператор switch-case на основе * fmt для набора текста, а затем использовать ** data в качестве двумерного массива для его печати.Используйте строки / столбцы в качестве индекса двумерного массива.

...