c - C:如何加载、存储和打印字符串和整数的混合矩阵
问题描述
我已经尝试解决一个问题一年多了,但似乎还没有解决问题的技能,所以我希望有人可以帮助我?我是堆栈溢出的新手,绝对是新手程序员。我正在使用 Codelite 作为编写和编译代码的程序。
在 CI 中,想在终端/屏幕上加载、保存和打印一个矩阵,但不仅仅是一个数字矩阵,而是一个字符串和数字的矩阵。如果这不可能,是否可以通过将值加载到单独的矩阵中来从包含名称和数字的文本文件(制表符分隔 (.txt) 或逗号分隔列表 (.csv))列表中加载值?
解决方案
为了了解您正在尝试做的事情,您必须了解 C 提供了哪些工具来帮助您将不同类型的数据作为一个单元进行协调。C 提供了一个结构(或结构)。每当您考虑必须在保留不同类型之间的关系的同时管理字符数据和数字数据时——想想 struct。
在这里,鉴于您显示的数据以及您尝试过的事实,您int column=4;
似乎有一个firstname
、lastname
、some_ID
和some_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;
现在我们有了一个结构,可以保存first
andlast
名称(每个 63 个字符 + nul 终止字符)和两个unsigned
值,我称之为id
and num
(因为没有更好的词)。同样,verifyunsigned
也适用于数字类型。一个 4 字节unsigned
可以处理来自 的值0 to 4294967295
,如果需要,可以选择一个新类型,例如,或者使用提供long long unsigned
的确切宽度uint64_t
stdint.h
有了这个,你剩下的挑战就很简单了。您声明一个数组person_t
(结构的数组),将每一行数据读入一个MAXC
大小的缓冲区(这应该绰绰有余,但无论如何都要通过strlen()
在每次读取后检查缓冲区并确保它小于MAXC-1
chars 和最后一个字符是'\n'
),并将每一行解析为first
、last
、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 */
上面,请注意,这n
只是一个记录计数器,每次向数组中添加first
、last
、id
和num
元素时都会增加,因此仅尝试读取 whilen < MAXPPL
可以防止您尝试存储超出空间的元素。在您的条件中包含返回fgets
保证您将仅尝试解析buf
ifbuf
已填充的值并包含要解析的有效字符。
这将我们带到了解析点。您已将您的行读入buf
并验证buf
了包含字符,但是如何将行拆分为多个部分,以便将您的first
、last
、id
和num
值存储在每个结构元素中?有很多方法可以做到这一点,您可以使用一对指针,然后沿着缓冲区检查分隔符,您可以strtok
使用正确的分隔符调用(或者strsep
如果 csv 中可能存在空字段,则使用文件),或者,这里最简单的可能只是用于sscanf
拆分buf
为first
、last
、id
,num
然后验证返回(发生了 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
如果您还有其他问题,请仔细查看并告诉我。
推荐阅读
- visual-studio - vs_buildtools.exe 不在容器中运行
- next.js - 使用 Next.js 支持旧版 url
- json - Tshark -T 输出为 json 或 ek
- android - 由于 minSdkVersion 正在努力让 Android 构建工作
- c++ - if 语句逻辑测试中的裸非布尔变量
- python - Dask df.to_parquet() OOM
- c++ - 为什么 bitset 会抛出 out_of_range 错误?
- amazon-web-services - 在 Fargate 上使用 eks 获取 aws 帐户 ID
- python - 将列表中的数据附加到新列表中,而这些数据不会出现在其他列表中
- macos - MacOS 真实睡眠检测