printf'ing矩阵

Fla*_*ius 3 c glibc

我正在尝试实现打印2D数据的通用功能.我想出的是:

int mprintf(FILE* f, char* fmt, void** data, size_t cols, size_t rows)
Run Code Online (Sandbox Code Playgroud)

我们面临的挑战是确定有多少位从一次读取data的基础上fmt.

格式fmt将是stdlib特定的格式printf()和类似.

你对stdlibc(GNU GCC C)已经存在的功能有什么了解吗?我可以用它来缓解这个问题吗?

我尝试避免必须手动完成所有操作,因为我知道"我很蠢"(我不想引入愚蠢的错误).因此,重用代码将是最无错误的方式.

谢谢

附录

我看到有一个/usr/include/printf.h.我不能使用任何这些功能来做到这一点并同时轻松完成我的工作吗?

Jon*_*ler 6

有问题的设计:

int mprintf(FILE *f, char *fmt, void **data, size_t cols, size_t rows);
Run Code Online (Sandbox Code Playgroud)

高级设计点

  1. 如果要打印8x8矩阵的4x4部分,则需要知道矩阵的行长度以及要打印的大小.或者您可能更喜欢将其作为单独的功能.
  2. 据推测,格式将定义矩阵条目之间的分隔,或者你会强制它们之间的空间,或者什么?(如果用户指定"%d",这些数字是否会连接在一起?)
  3. 你隐含地假设矩阵将自己打印,在页面上左对齐.您如何调整界面以在其他地方打印矩阵?线路上的领先空间?矩阵的每一行之前的文字?矩阵后面的文字?

低级设计点

  1. 格式字符串应为a const char *.

  2. 显然,您的代码可以做printf()或多或少做的事情.它查看格式转换说明符,然后确定要收集的类型.在某些方面,您的代码会稍微复杂一些.你需要治疗的阵列unsigned char由阵列不同short,等等C99提供了修饰hhsigned charunsigned char(格式说明符之前d,i,o,u,x,或X),并修改hshortunsigned short.你也应该认识到这些.同样,应该处理Lfor long doublelfor longllfor 的修饰符long long.有趣的是,printf()不必处理float(因为任何单个float值会自动提升double),但您的代码必须这样做.通过类比hL,你应该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); 
    
    Run Code Online (Sandbox Code Playgroud)

    这会将data参数更改为a void *.也许我太不含咖啡因,但我看不出如何使双指针工作得当.

大纲

  • 确定元素的大小和正确的格式字符串.
  • 对于每一行
    • 对于每一列
    • 查找元素的数据
    • 调用适当的函数进行打印
    • 如果需要,打印分隔符
    • 打印换行符

插图

此代码假定"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...
}
Run Code Online (Sandbox Code Playgroud)

工作代码

作为练习留下:

  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 */
Run Code Online (Sandbox Code Playgroud)

  • 如果我们建立一个"官方"名人堂,我提名这个答案. (2认同)