从 stdin 读取未知长度的未知行数
Reading an unknown number of lines with unknown length from stdin
我是 C 语言编程的新手,我正在尝试使用 fgets
[=24 从 stdin
读取输入=].
首先,我想阅读最多 50 行,每行最多 50 个字符,并且有如下内容:
int max_length = 50;
char lines[max_length][max_length];
char current_line[max_length];
int idx = 0;
while(fgets(current_line, max_length, stdin) != NULL) {
strcopy(lines[idx], current_line);
idx++;
}
上面的代码片段成功读取输入并将其存储到 lines
数组中,我可以在其中排序和打印它。
我的问题是如何处理未知行数,每行字符数未知? (请记住,我将不得不对这些行进行排序并打印出来)。
检查发现 here 的 GetString 函数。
虽然已经回答了这个问题的许多不同变体,但是关于如何解决这个问题的考虑可以用一个段落。遇到这个问题时,无论您使用哪种库或 POSIX 函数组合,方法都是相同的。
本质上,您将动态分配合理数量的字符来容纳每一行。 POSIX getline
会自动为你做这件事,使用 fgets
你可以简单地读取一个充满字符的固定缓冲区并附加它们(根据需要重新分配存储)直到 '\n'
字符已读取(或达到 EOF
)
如果你使用getline
,那么你必须分配内存,并复制缓冲区填充。否则,您将用读取的每一行新行覆盖以前的行,并且当您尝试 free
每行时,您可能会在重复尝试时出现 double-free 或 corruption 的 SegFault释放相同的内存块。
您可以使用strdup
简单地复制缓冲区。但是,由于 strdup
分配存储空间,您应该先验证分配是否成功,然后再将指向新内存块的指针分配给您的行集合。
要访问每一行,您需要一个指向每一行开头的指针(保存每一行的内存块)。通常使用指向 char 的指针的 指针。 (例如 char **lines;
)内存分配通常通过分配一些合理数量的指针来处理,跟踪您使用的数量,当您达到您分配的数量时,您 realloc
并加倍指针的数量。
与每次读取一样,您需要验证每个内存分配。 (每个 malloc
、calloc
或 realloc
)您还需要通过内存错误检查程序 运行 验证您的程序使用您分配的内存的方式(例如 valgrind
对应 Linux)。它们使用简单,只需 valgrind yourexename
.
将这些部分放在一起,您可以执行类似于以下操作的操作。下面的代码将从作为程序的第一个参数提供的文件名中读取所有行(如果没有提供参数,则默认从 stdin
读取)并打印行号和行到 stdout
(保留那个请记住,如果你 运行 它在一个 50,000 行的文件中)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 8
int main (int argc, char **argv) {
size_t ndx = 0, /* line index */
nptrs = NPTR, /* initial number of pointers */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (no. of chars read by getline) */
char *line = NULL, /* buffer to read each line */
**lines = NULL; /* pointer to pointer to each line */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate initial 'nptrs' pointers */
if (!(lines = calloc (nptrs, sizeof *lines))) {
fprintf (stderr, "error: memory exhausted - lines.\n");
return 1;
}
/* read each line with POSIX getline */
while ((nchr = getline (&line, &n, fp)) != -1) {
if (nchr && line[nchr - 1] == '\n') /* check trailing '\n' */
line[--nchr] = 0; /* overwrite with nul-char */
char *buf = strdup (line); /* allocate/copy line */
if (!buf) { /* strdup allocates, so validate */
fprintf (stderr, "error: strdup allocation failed.\n");
break;
}
lines[ndx++] = buf; /* assign start address for buf to lines */
if (ndx == nptrs) { /* if pointer limit reached, realloc */
/* always realloc to temporary pointer, to validate success */
void *tmp = realloc (lines, sizeof *lines * nptrs * 2);
if (!tmp) { /* if realloc fails, bail with lines intact */
fprintf (stderr, "read_input: memory exhausted - realloc.\n");
break;
}
lines = tmp; /* assign reallocted block to lines */
/* zero all new memory (optional) */
memset (lines + nptrs, 0, nptrs * sizeof *lines);
nptrs *= 2; /* increment number of allocated pointers */
}
}
free (line); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free memory for each line */
}
free (lines); /* free pointers */
return 0;
}
如果您没有 getline
或 strdup
,您可以轻松实现每一个。每个网站上都有多个示例。如果找不到,请告诉我。如果您还有其他问题,请告诉我。
我是 C 语言编程的新手,我正在尝试使用 fgets
[=24 从 stdin
读取输入=].
首先,我想阅读最多 50 行,每行最多 50 个字符,并且有如下内容:
int max_length = 50;
char lines[max_length][max_length];
char current_line[max_length];
int idx = 0;
while(fgets(current_line, max_length, stdin) != NULL) {
strcopy(lines[idx], current_line);
idx++;
}
上面的代码片段成功读取输入并将其存储到 lines
数组中,我可以在其中排序和打印它。
我的问题是如何处理未知行数,每行字符数未知? (请记住,我将不得不对这些行进行排序并打印出来)。
检查发现 here 的 GetString 函数。
虽然已经回答了这个问题的许多不同变体,但是关于如何解决这个问题的考虑可以用一个段落。遇到这个问题时,无论您使用哪种库或 POSIX 函数组合,方法都是相同的。
本质上,您将动态分配合理数量的字符来容纳每一行。 POSIX getline
会自动为你做这件事,使用 fgets
你可以简单地读取一个充满字符的固定缓冲区并附加它们(根据需要重新分配存储)直到 '\n'
字符已读取(或达到 EOF
)
如果你使用getline
,那么你必须分配内存,并复制缓冲区填充。否则,您将用读取的每一行新行覆盖以前的行,并且当您尝试 free
每行时,您可能会在重复尝试时出现 double-free 或 corruption 的 SegFault释放相同的内存块。
您可以使用strdup
简单地复制缓冲区。但是,由于 strdup
分配存储空间,您应该先验证分配是否成功,然后再将指向新内存块的指针分配给您的行集合。
要访问每一行,您需要一个指向每一行开头的指针(保存每一行的内存块)。通常使用指向 char 的指针的 指针。 (例如 char **lines;
)内存分配通常通过分配一些合理数量的指针来处理,跟踪您使用的数量,当您达到您分配的数量时,您 realloc
并加倍指针的数量。
与每次读取一样,您需要验证每个内存分配。 (每个 malloc
、calloc
或 realloc
)您还需要通过内存错误检查程序 运行 验证您的程序使用您分配的内存的方式(例如 valgrind
对应 Linux)。它们使用简单,只需 valgrind yourexename
.
将这些部分放在一起,您可以执行类似于以下操作的操作。下面的代码将从作为程序的第一个参数提供的文件名中读取所有行(如果没有提供参数,则默认从 stdin
读取)并打印行号和行到 stdout
(保留那个请记住,如果你 运行 它在一个 50,000 行的文件中)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 8
int main (int argc, char **argv) {
size_t ndx = 0, /* line index */
nptrs = NPTR, /* initial number of pointers */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (no. of chars read by getline) */
char *line = NULL, /* buffer to read each line */
**lines = NULL; /* pointer to pointer to each line */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate initial 'nptrs' pointers */
if (!(lines = calloc (nptrs, sizeof *lines))) {
fprintf (stderr, "error: memory exhausted - lines.\n");
return 1;
}
/* read each line with POSIX getline */
while ((nchr = getline (&line, &n, fp)) != -1) {
if (nchr && line[nchr - 1] == '\n') /* check trailing '\n' */
line[--nchr] = 0; /* overwrite with nul-char */
char *buf = strdup (line); /* allocate/copy line */
if (!buf) { /* strdup allocates, so validate */
fprintf (stderr, "error: strdup allocation failed.\n");
break;
}
lines[ndx++] = buf; /* assign start address for buf to lines */
if (ndx == nptrs) { /* if pointer limit reached, realloc */
/* always realloc to temporary pointer, to validate success */
void *tmp = realloc (lines, sizeof *lines * nptrs * 2);
if (!tmp) { /* if realloc fails, bail with lines intact */
fprintf (stderr, "read_input: memory exhausted - realloc.\n");
break;
}
lines = tmp; /* assign reallocted block to lines */
/* zero all new memory (optional) */
memset (lines + nptrs, 0, nptrs * sizeof *lines);
nptrs *= 2; /* increment number of allocated pointers */
}
}
free (line); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free memory for each line */
}
free (lines); /* free pointers */
return 0;
}
如果您没有 getline
或 strdup
,您可以轻松实现每一个。每个网站上都有多个示例。如果找不到,请告诉我。如果您还有其他问题,请告诉我。