首页 > 解决方案 > C:如何加载、存储和打印字符串和整数的混合矩阵

问题描述

我已经尝试解决一个问题一年多了,但似乎还没有解决问题的技能,所以我希望有人可以帮助我?我是堆栈溢出的新手,绝对是新手程序员。我正在使用 Codelite 作为编写和编译代码的程序。

在 CI 中,想在终端/屏幕上加载、保存和打印一个矩阵,但不仅仅是一个数字矩阵,而是一个字符串和数字的矩阵。如果这不可能,是否可以通过将值加载到单独的矩阵中来从包含名称和数字的文本文件(制表符分隔 (.txt) 或逗号分隔列表 (.csv))列表中加载值?

这是我想加载到 C 程序中的矩阵示例(图像)

这是我到目前为止的代码示例(图片)

标签: cstringmatrixcharacter

解决方案


为了了解您正在尝试做的事情,您必须了解 C 提供了哪些工具来帮助您将不同类型的数据作为一个单元进行协调。C 提供了一个结构(或结构)。每当您考虑必须在保留不同类型之间的关系的同时管理字符数据和数字数据时——想想 struct。

在这里,鉴于您显示的数据以及您尝试过的事实,您int column=4;似乎有一个firstnamelastnamesome_IDsome_num与每条数据记录相关联。这将直接借给包含两个字符数组和两个数值的结构。(例如,我们将简单地使用具有自动存储功能的固定大小的数组来存储名称——但您可以自由地简单地声明指针并根据需要为每个名称分配)。

首先,#define我们需要使用常量,所以我们没有在代码中使用幻数(但请注意,scanf 字段宽度修饰符不能是变量,所以这是一个例外):

/* if you need a constant #define one (or more) */
#define MAXPPL   16     /* max people (e.g. number of elements in array) */
#define MAXNM    64     /* max name length for both first, last */
#define MAXC   1024     /* max characters for read buffer (per-line) */

(不要吝啬缓冲区大小,如果你的名字可以接近 64 个字符,那么大小加倍)

现在让我们定义我们的结构,使用 atypedef作为一种方便的方式,我们可以将person_t其用作类型,而不必在需要类型struct person的任何地方都键入,例如

typedef struct person { /* use a struct to coordinate differing types */
    char first[MAXNM],
         last[MAXNM];
    unsigned id,
             num;
} person_t;

现在我们有了一个结构,可以保存firstandlast名称(每个 63 个字符 + nul 终止字符)和两个unsigned值,我称之为idand num(因为没有更好的词)。同样,verifyunsigned也适用于数字类型。一个 4 字节unsigned可以处理来自 的值0 to 4294967295,如果需要,可以选择一个新类型,例如,或者使用提供long long unsigned的确切宽度uint64_tstdint.h

有了这个,你剩下的挑战就很简单了。您声明一个数组person_t(结构的数组),将每一行数据读入一个MAXC大小的缓冲区(这应该绰绰有余,但无论如何都要通过strlen()在每次读取后检查缓冲区并确保它小于MAXC-1chars 和最后一个字符是'\n'),并将每一行解析为firstlastidnum,将解析后的值存储在数组中,同时确保不会尝试在数组中存储比声明更多的名称和数字。

例如,要MAXPPL在读取每一行数据时填充数组的元素,您可以执行以下操作:

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

    char buf[MAXC] = "";                            /* read buffer  */
    unsigned n = 0;                                 /* person count */
    person_t people[MAXPPL] = {{ .first = "" }};    /* array of people */

    /* 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;
    }

    /* fill up to MAXPPL struct while reading each line of data */
    while (n < MAXPPL && fgets (buf, MAXC, fp)) {
        /* temp struct to hold parsed values */
        person_t tmp = { .first = "" };

        /* validate entire line read here - left to you */

上面,请注意,这n只是一个记录计数器,每次向数组中添加firstlastidnum元素时都会增加,因此仅尝试读取 whilen < MAXPPL可以防止您尝试存储超出空间的元素。在您的条件中包含返回fgets保证您将仅尝试解析bufifbuf已填充的值并包含要解析的有效字符。

这将我们带到了解析点。您已将您的行读入buf并验证buf了包含字符,但是如何将行拆分为多个部分,以便将您的firstlastidnum值存储在每个结构元素中?有很多方法可以做到这一点,您可以使用一对指针,然后沿着缓冲区检查分隔符,您可以strtok使用正确的分隔符调用(或者strsep如果 csv 中可能存在空字段,则使用文件),或者,这里最简单的可能只是用于sscanf拆分buffirstlastidnum然后验证返回(发生了 4 次转换)

您还提到了具有逗号分隔值文件或制表符分隔文件的可能性,您可以使用一个简单的方法处理它if ... else if ....,首先检查逗号分隔值,然后是空格分隔值(反之亦然 - 您的选择),例如

        /* attempt comma separated parse */
        if (sscanf (buf, "%63[^,],%63[^,],%u,%u", 
                    tmp.first, tmp.last, &tmp.id, &tmp.num) == 4)
            people[n++] = tmp;
        /* attempt space/tab separated parse */
        else if (sscanf (buf, "%63s %63s %u %u",
                    tmp.first, tmp.last, &tmp.id, &tmp.num) == 4)
            people[n++] = tmp;
    }
    if (fp != stdin) fclose (fp);       /* close file if not stdin */

注意:在两个调用之间使用不同的转换说明符sscanf——确保你理解原因,请参阅scanf(3) - Linux 手册页

此时,您已将所有数据读入您的结构数组,并且您可以随意使用它来做您需要的事情。填充数据的元素数量存储在n. 因此,您可以使用简单的for循环以“矩阵”形式简单地输出数据,例如

    for (unsigned i = 0; i < n; i++)    /* output results */
        printf ("%-10s %-10s %10u %10u\n", people[i].first, people[i].last,
                people[i].id, people[i].num);

总而言之,您将拥有:

#include <stdio.h>

/* if you need a constant #define one (or more) */
#define MAXPPL   16     /* max people (e.g. number of elements in array) */
#define MAXNM    64     /* max name length for both first, last */
#define MAXC   1024     /* max characters for read buffer (per-line) */

typedef struct person { /* use a struct to coordinate differing types */
    char first[MAXNM],
         last[MAXNM];
    unsigned id,
             num;
} person_t;

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

    char buf[MAXC] = "";                            /* read buffer  */
    unsigned n = 0;                                 /* person count */
    person_t people[MAXPPL] = {{ .first = "" }};    /* array of people */

    /* 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;
    }

    /* fill up to MAXPPL struct while reading each line of data */
    while (n < MAXPPL && fgets (buf, MAXC, fp)) {
        /* temp struct to hold parsed values */
        person_t tmp = { .first = "" };

        /* validate entire line read here - left to you */

        /* attempt comma separated parse */
        if (sscanf (buf, "%63[^,],%63[^,],%u,%u", 
                    tmp.first, tmp.last, &tmp.id, &tmp.num) == 4)
            people[n++] = tmp;
        /* attempt space/tab separated parse */
        else if (sscanf (buf, "%63s %63s %u %u",
                    tmp.first, tmp.last, &tmp.id, &tmp.num) == 4)
            people[n++] = tmp;
    }
    if (fp != stdin) fclose (fp);       /* close file if not stdin */

    for (unsigned i = 0; i < n; i++)    /* output results */
        printf ("%-10s %-10s %10u %10u\n", people[i].first, people[i].last,
                people[i].id, people[i].num);

    return 0;
}

示例输入文件

逗号分隔

$ cat dat/namesnumbers.txt
John,Smith,135897,587555
Kelly,Johnson,46359,7811
Sam,Samson,78555279,4525
Thomas,Jefferson,765,31

制表符分隔

$ cat dat/namesnumberstab.txt
John    Smith   135897  587555
Kelly   Johnson 46359   7811
Sam     Samson  78555279        4525
Thomas  Jefferson       765     31

示例使用/输出

无论输入文件如何,输出都是相同的。

$ ./bin/namesnumbers dat/namesnumbers.txt
John       Smith          135897     587555
Kelly      Johnson         46359       7811
Sam        Samson       78555279       4525
Thomas     Jefferson         765         31

如果您还有其他问题,请仔细查看并告诉我。


推荐阅读