C:如何加载,存储和打印字符串和整数的混合矩阵

时间:2018-08-05 16:36:48

标签: c string matrix character

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

在C语言中,我想将矩阵加载,保存并打印到终端/屏幕中,不仅是数字矩阵,还包括字符串和数字矩阵。如果这不可能,则可以通过将值加载到单独的矩阵中来从包含名称和数字的文本文件(制表符分隔的(.txt)或逗号分隔的列表(.csv))列表中加载值?

Here is a sample of a matrix that I would like to load into the C program (image)

This is a sample of the code that I have so far (image)

2 个答案:

答案 0 :(得分:0)

为了使您的头想做什么,您必须了解C提供了哪些工具来帮助您将不同类型的数据作为一个单元进行协调。 C提供了一个结构(或struct)。任何时候考虑要在保持不同类型之间的关系的同时管理字符数据和数字数据时,请考虑struct。

在这里,根据您显示的数据和您尝试过int column=4;的事实,您似乎有一个firstnamelastnamesome_ID和{{1 }}与每个数据记录相关联。这将直接导致包含两个字符数组和两个数值的结构。 (出于示例目的,我们将简单地使用具有自动存储功能的固定大小的数组来存储名称-但您可以随意声明指针并为每个名称分配名称)。

首先,让我们some_num使用我们需要使用的常量,因此我们不在代码中使用 magic-numbers (但请注意,#define 字段宽度修饰符不能是变量,因此是一个例外):

scanf

(不要忽略缓冲区的大小,如果您的名字可以接近64个字符,则将大小加倍)

现在让我们定义一个结构,使用/* 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作为类型,而不必在需要该类型的任何地方键入person_t,例如

struct person

现在我们有了一个结构,可以容纳typedef struct person { /* use a struct to coordinate differing types */ char first[MAXNM], last[MAXNM]; unsigned id, num; } person_t; first名称(每个63个字符+ 以n终止的字符)和两个last值,我称之为unsignedid(因为缺少更好的词)。同样,请验证num是否适用于数字类型。 4字节的unsigned可以处理unsigned中的值,并根据需要选择新的类型,例如0 to 4294967295或使用long long unsigned

中提供的确切宽度uint64_t

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

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

num

上面,请注意,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 */ nfirstlast元素就会增加数组,因此只有在id时尝试读取才能保护您避免存储超出您的存储空间的元素。在您的情况下包括返回num的情况,可以保证只有在n < MAXPPL已被填充并且包含要解析的有效字符的情况下,才尝试从fgets解析值。

这使我们到达了解析点。您已经将行读入buf并验证了buf包含字符,但是如何将行拆分成几段,以便您拥有bufbuf,{{1} }和first值存储在每个struct元素中?有多种方法可以执行此操作,可以使用一对指针,然后沿着缓冲区检查分隔符,可以使用适当的分隔符调用last(如果存在分隔符,则可以使用id可能是csv文件中字段为空),或者,也许最简单的方法是使用numstrtok拆分为strsepsscanf,{ {1}}和buf,然后验证返回值(发生了4次会话)

您还提到了使用逗号分隔值文件或使用制表符分隔文件的可能性,您可以使用简单的first处理这些文件,其中您先检查逗号分隔值,然后再检查空白分隔值(反之亦然-您的选择),例如

last

注意:在两个id调用之间使用不同的转化说明符-确保您了解原因,请参见scanf(3) - Linux manual page

到那时,您已将所有数据读入struct数组,您可以根据需要随意处理它。充满数据的元素数量存储在num中。因此,您可以使用简单的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

示例输入文件

逗号分隔

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);

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:-2)

我建议应用此处描述的概念,这对实现您的最终目标很有帮助:
https://ericlippert.com/2014/03/21/find-a-simpler-problem/

在您的情况下,我将采用这种方式:

  • 制作一个可以读取两种输入之一的程序,
    数字或字符串,请选择一个简单的
  • 然后更改为加载另一种,难度更大;
    我认为加载字符串比较困难,但是您的意见很重要
  • 然后升级以从文件中的简单列表中加载更简单的文件,
    即一维
  • 然后切换到加载更硬的1D
  • 然后升级为更容易地加载一个2D,但只有一种,并且总是一样
  • 然后切换为更难地加载一个2D,仍然只有一种,总是一样
  • 现在您可以加载两个版本。分析您的输入。您能否始终预测接下来要阅读哪种类型?在您的示例中,看起来总是这样,两个sring,两个数字。
  • 如果您总是可以预测接下来要加载的种类,请混合使用两个版本的2D加载代码,并确保接下来始终以正确的类型读取代码。

还有一些其他提示,这些提示似乎与您相关(如您所显示的代码所隐含):

确保将字符串读入足够大的缓冲区中。在2D矩阵的每个像元中只有一个单字符缓冲区:

char t[r][c];

您明智地避免使用scanf(),那很好。但是,即使使用fsanf(),也需要多加注意。始终检查返回值,它会告诉您扫描的距离。如果未成功,请确保您可以解决该问题。

这是一篇有关扫描的有用文章,通过试图说服您不要使用scanf(),它实际上教会了有关如何使用它的非常有用的知识。它还建议替代方法。最重要的是读取整行并将其解析为大字符串。

http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html

让我知道您是否无法预测下一种输入。这将更加困难,但是也许我仍然可以推荐一些东西。请提供更多有关它如何变化的详细信息,并提供更多示例(不同模式的示例)以证明您输入的多样性。