gpt4 book ai didi

c - 将文本文件读入C中的2D数组

转载 作者:太空宇宙 更新时间:2023-11-04 07:52:34 24 4
gpt4 key购买 nike

我正在尝试将整个文本文件读入一个2D数组,这样我就可以限制它的存储量,并知道何时执行新行(如果有人有更好的主意,我愿意接受建议)。
这就是我目前所拥有的:

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

char texto[15][45];
char ch;
int count = 0;
FILE *f = fopen("texto.txt", "r");

if(f == NULL)
printf("ERRO ao abrir o ficheiro para leitura");

while((ch = fgetc(f) != EOF))
count++;

rewind(f);

int tamanho = count;

texto = malloc(tamanho *sizeof(char));

fscanf(f, "%s", texto);

fclose(f);

printf("%s", texto);

return (EXIT_SUCCESS);
}

文本文件是这样的
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip
lorem ipsum lorem ipsum lorem ipsum lorem ip

但我知道这个错误
错误:为数组类型的表达式赋值
在这里
texto=malloc(tamanho*sizeof(char));

最佳答案

您所面临的问题之一是迫使您理解面向字符的输入、格式化输入和面向行的输入之间的区别和限制。将数组限制设置为:

char texto[15][45];

上面声明了一个由15-1D个数组组成的数组,每个数组包含45个字符,每个字符在内存中是连续的(定义为 array)。这意味着在每个索引 texto[0] - texto[14]处最多可以存储 45个字符(或后跟nul终止字符的 44个字符的字符串)。
然后给你一个文件,每个文件有7行 45字符。但每行只有 44个字符?--错了。由于(假定给定 "texto.txt")信息保存在文本文件中,因此在每行的末尾将有一个额外的 '\n'(换行)字符。你必须解释它在读取文件时的存在。文件中的每一行将如下所示:
        10        20        30        40
123456789012345678901234567890123456789012345
lorem ipsum lorem ipsum lorem ipsum lorem ip\n

(其中数字只是表示一个刻度,显示每行中有多少个字符)
ASCII '\n'字符是单个字符。
格式化输入法
您能用转换说明符用 fscanf读取输入吗?(回答:不)为什么? "%s"转换说明符在读取非空白字符后遇到第一个空白字符时停止读取。这意味着在第5个字符之后,使用 "%s"的读取将停止读取。
尽管您可以使用 fscanf (fp, "%s", ...)格式的字符类转换说明符(括号中包含要包含的字符,或者如果类中的第一个字符 [...],则排除该字符)来解决此问题,但您可以将 '^'字符保留在输入流中未读。
虽然可以通过使用 '\n'赋值抑制字符读取并丢弃带有 '*'的下一个字符(换行符)来解决此问题,但如果行中有任何其他字符,它们也将保留在未读的输入缓冲区(输入流,例如您的文件)中。
您是否开始意识到,使用 "%*c"函数家族进行文件输入本质上是脆弱的?(你说得对)
使用 scanf的简单实现可以是:
#include <stdio.h>

#define NROWS 15 /* if you need a constant, #define one (or more) */
#define NCOLS 45

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

char texto[NROWS][NCOLS] = {""};
size_t n = 0;
/* 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;
}

/* read up to NROWS lines of 44 char each with at most 1 trailing char */
while (n < NROWS && fscanf (fp, "%44[^\n]%*c", texto[n]) == 1)
n++; /* increment line count */

if (fp != stdin) fclose (fp); /* close file if not stdin */

for (size_t i = 0; i < n; i++) /* output lines stored */
printf ("texto[%2lu]: '%s'\n", i, texto[i]);

return 0;
}

(注意:如果你能保证你的输入文件格式是固定的并且不会改变,那么这是一个合适的方法。但是,文件中的一个附加杂散字符可能会破坏这种方法)
示例使用/输出
$ ./bin/texto2dfscanf <dat/texto.txt
texto[ 0]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 1]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 2]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 3]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 4]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 5]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'
texto[ 6]: 'lorem ipsum lorem ipsum lorem ipsum lorem ip'

面向行的输入
更好的方法总是面向行的方法。为什么?它允许您分别验证从文件(或从用户)读取的数据行,然后验证从该行解析必要信息。
但是, fscanf的大小有一个故意的陷阱,使简单的面向行的方法复杂化。虽然您可能只是尝试将每一行文本读入 texto,但您只需将文本读入 texto[0-14],而不阅读 texto。(什么?我认为面向行的输入可以处理这个问题?--如果您在试图填充的缓冲区中提供了足够的空间…)
面向行的输入函数( '\n'和POSIX fgets)读取尾随的 getline并将其包含到正在填充的缓冲区中——前提是有足够的空间。如果使用 '\n'fgets将只读取缓冲区中指定的字符(它提供对数组边界的保护)。您在这里的任务旨在要求使用面向行的函数读取 fgets字符,以便读取:
the text + '\n' + '\0'

(文本加新行加上nul终止字符)
这迫使您正确地进行面向行的输入。将信息读入一个足够大的缓冲区,以处理最大的预期输入行(不要忽略缓冲区大小)。验证您的读取成功。然后使用您选择的任何方式从行中解析您需要的信息(在本例中 46是很好的)。通过这两步操作,您可以读取行,确定读取行的原始长度(包括 sscanf),并验证它是否都适合您的缓冲区。然后可以解析 '\n'字符(加上nul终止字符的空间)。
此外,如果其他字符仍然未读,您可以预先知道,然后继续阅读并丢弃剩余字符,为下次阅读做好准备。
一个合理的面向行的方法可能如下所示:
#include <stdio.h>
#include <string.h>

#define NROWS 15 /* if you need a constant, #define one (or more) */
#define NCOLS 45
#define MAXC 1024

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

char texto[NROWS][NCOLS] = {""},
buffer[MAXC] = "";
size_t n = 0;
/* 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;
}

while (n < NROWS && fgets (buffer, MAXC, fp)) {
size_t len = strlen (buffer);
if (len && buffer[len-1] == '\n')
buffer[--len] = 0;
else
if (len == MAXC-1) {
fprintf (stderr, "error: line %zu too long.\n", ++n);
/* remove remaining chars in line before next read */
while (fgets (buffer, MAXC, fp)) {}
}
if (sscanf (buffer, "%44[^\n]", texto[n]) == 1)
n++;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */

for (size_t i = 0; i < n; i++) /* output lines stored */
printf ("texto[%2zu]: '%s'\n", i, texto[i]);

return 0;
}

(输出相同)
面向字符的输入
剩下的唯一方法是面向字符的方法(这是逐字符读取文件的非常有效的方法)。面向字符的方法的唯一挑战是逐个字符地跟踪索引。这里的方法很简单。只需反复调用 44填充 fgetc中的可用字符,然后丢弃行中的任何其他字符,直到达到 texto'\n'为止。在正确的情况下,与面向行的方法相比,它实际上可以提供更简单但同样健壮的解决方案。我会把调查这个方法留给你。
C中任何输入任务的关键是将正确的工具集与作业匹配。如果保证输入文件的格式是固定的,不会有任何偏差,那么格式化的输入就可以生效。对于所有其他输入(包括用户输入),通常建议使用面向行的输入,因为它能够读取整行,而不会在未读的输入缓冲区中留下一个悬挂的 EOF——前提是您使用了足够大的缓冲区。可以使用面向字符的输入,但您还需要在按字符的基础上跟踪索引。使用这三种方法是了解哪一种方法是工作的最佳工具的唯一途径。
再看一遍,如果你还有问题,请告诉我。

关于c - 将文本文件读入C中的2D数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52901438/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com