将 getline 与动态存储一起使用
Using getline along with dynamic storage
我有以下片段。我有 across 几个示例,其中可以使用 getline 读取一行,然后简单地打印它。我正在努力不断地从 stdin 保存,使用 getline() 读取并且可能有某种缓冲区来跟踪所有读取的内容(是否需要?)。
最后,我只是按照用户输入最后一行的倒序打印内容。
我不确定 char* 是否可以指向整个缓冲区 os stdin 输入,我们最终可以反向读取。
这是 运行 分段错误,我最好的猜测是访问从未分配的内存。
size_t linecount = 0;
ssize_t bytes_read;
size_t nbytes = 100;
char *content;
if (1) {
my_string = (char*) malloc(nbytes + 1);
while ((bytes_read = getline(&my_string, &nbytes, stdin)) >= 0
&& my_string[0] != '\n') {
puts(my_string);
printf("read: %ld bytes", bytes_read);
content = (char*) malloc((strlen(my_string) + 1) * sizeof(char));
success = content != NULL;
if (success) {
strcpy(content, my_string);
++linecount;
} else {
printf("Malloc error\n");
exit(1);
}
}
}
使用 getline()
读取,如果您提供初始化为 NULL
的指针并且大小参数初始化为零,那么 getline()
将为您的输入分配足够的存储空间——无论长度如何.如果您只是简单地反转字符串,则没有理由分配额外的存储空间。如果要在集合中保存由 getline()
读取的多行(例如使用指针数组,例如 char *lines[NLINES];
或指向指针的指针,例如 char **lines;
)
要使用 getline()
读取所有输入行,打开文件后(或简单地将 stdin
分配给 FILE*
指针),您需要不超过:
char *lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0; /* n set to 0, getline() allocates */
...
while (getline (&lineptr, &n, fp) != -1) /* read every line from file */
...
如果只是简单的将每一行的字符反转,可以写一个简单的字符串反转函数,输出反转后的字符串。 getline()
读取循环和一个名为 strrev()
的函数将字符串作为参数:
while (getline (&lineptr, &n, fp) != -1) /* read every line from file */
fputs (strrev (lineptr), stdout); /* reverse line & output */
制作你的 strrev()
函数来处理字符串,而不管 '\n'
是否存在允许你简单地传递来自 getline()
的包含 '\n'
字符的行最后,不必先 trim 字符串中的换行符。如果您确实想删除 '\n'
,您可以使用 getline()
返回的字符数,或者只需要简单地调用 strcspn()
,例如
while (getline (&lineptr, &n, fp) != -1) { /* read every line from file */
lineptr[strcspn (lineptr, "\n")] = 0; /* trim '\n' from end of lineptr */
/* do whatever else is desired */
}
把它放在一起,并将要读取的文件名作为程序的第一个参数(如果没有给出参数,则默认从 stdin
读取),你可以这样做:
#define _GNU_SOURCE /* getline() is POSIX not standard C */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strrev (char *str); /* function to reverse string in-place */
/* (str must be mutable, not a string-literal) */
int main (int argc, char **argv) {
char *lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0; /* n set to 0, getline() allocates */
/* 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 (getline (&lineptr, &n, fp) != -1) /* read every line from file */
fputs (strrev (lineptr), stdout); /* reverse line & output */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
free (lineptr); /* getline() allocates, so don't forget to free the memory */
}
/** strrev - reverse string, swaps src & dest each iteration up to '\n'.
* Takes valid string and reverses, original is not preserved.
* If str is valid, returns pointer to str, NULL otherwise.
*/
char *strrev (char *str)
{
if (!str) { /* validate str not NULL */
printf ("%s() error: invalid string\n", __func__);
return NULL;
}
if (!*str) /* check empty string */
return str;
char *begin = str,
*end = begin + strcspn (str, "\n") - 1;
while (end > begin)
{
char tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
return str;
}
例子Use/Output
从 stdin
读取并在完成后使用 Ctrl + d(Ctrl + z on windows) :
$ ./bin/getline_strrev
My dog has fleas
saelf sah god yM
My cat has none!
!enon sah tac yM
Lucky cat...
...tac ykcuL
从文件读取(或重定向)输入:
输入文件
$ cat dat/dog.txt
My dog has fleas
My cat has none!
Lucky cat...
例子Use/Output
$ ./bin/getline_strrev dat/dog.txt
saelf sah god yM
!enon sah tac yM
...tac ykcuL
重定向文件 stdin
:
$ ./bin/getline_strrev < dat/dog.txt
saelf sah god yM
!enon sah tac yM
...tac ykcuL
反转行而不是字符
根据您在评论中的说明,您可以通过分配指针(并根据需要重新分配)并为每行分配存储空间,分配块的地址来保存每行,从而存储未知长度的未知行数您的每个指针依次复制 getline()
读取的行到分配的块。您有两种类型的内存可以满足,内存块包含指针,内存块包含每一行。 How to read a text file and store in an array in C 一两天前对此进行了非常详细的解释。
该示例与此处的唯一区别是使用 getline()
而不是 fgets()
以及如何计算每行中的字符数并删除结尾 [=缓冲区中的 33=] 略有不同,只是由于 getline()
返回读取的字符数。颠倒行的顺序也不同(但也是微不足道的区别)
getline()
和反转行的变化是:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTRS 2 /* initial number of pointers to allocated */
int main (int argc, char **argv) {
char **lines = NULL, /* pointer to all lines */
*lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0, /* n set to 0, getline() allocates */
nptrs = NPTRS, /* no. of pointers to allocate */
used = 0; /* no. of pointers used */
ssize_t nchr; /* no. of chars read by getline() */
/* 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;
}
/* allocate initial nptrs pointers - validate EVERY allocation */
if ((lines = malloc (nptrs * sizeof *lines)) == NULL) {
perror ("malloc-lines");
return 1;
}
/* read every line from file, saving characters read in nchr */
while ((nchr = getline (&lineptr, &n, fp)) != -1) {
if (nchr > 0 && lineptr[nchr-1] == '\n') /* if chars read and \n */
lineptr[--nchr] = 0; /* trim \n from end */
if (used == nptrs) {
/* always realloc using a temporary pointer */
void *tmp = realloc (lines, (2 * nptrs) * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* don't exit, lines still good */
}
lines = tmp; /* assign reallocated block */
nptrs *= 2; /* update number of pointers */
}
/* allocate storage for line, assign address for new block to lines[used] */
if (!(lines[used] = malloc (nchr + 1))) {
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], lineptr, nchr + 1); /* copy line to new block of mem */
used += 1; /* increment used pointer counter */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
/* output lines in reverse order */
while (used--) {
puts (lines[used]);
free (lines[used]); /* free storage for each string when done */
}
free (lines); /* free pointers */
free (lineptr); /* free memory allocated by getline() */
}
例子Use/Output
与上述示例相同的 inpt 文件。
$ ./bin/getline_linerev dat/dog.txt
Lucky cat...
My cat has none!
My dog has fleas
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/getline_linerev dat/dog.txt
==6502== Memcheck, a memory error detector
==6502== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6502== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6502== Command: ./bin/getline_linerev dat/dog.txt
==6502==
Lucky cat...
My cat has none!
My dog has fleas
==6502==
==6502== HEAP SUMMARY:
==6502== in use at exit: 0 bytes in 0 blocks
==6502== total heap usage: 9 allocs, 9 frees, 5,887 bytes allocated
==6502==
==6502== All heap blocks were freed -- no leaks are possible
==6502==
==6502== For counts of detected and suppressed errors, rerun with: -v
==6502== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题或需要其他帮助,请告诉我。
由于目标是“以用户输入的相反顺序打印内容,最后一行在前”,程序必须存储所有行。 getline()
函数通常为每一行分配相当大的 space(在我的 Mac 上默认为 128 字节,如果输入行长于此则增长),因此通常最好有一个由 getline()
管理的缓冲区,如果需要可以增长,并以所需的长度将实际输入字符串复制到其他地方。我使用 strdup()
复制行。
/* Read file and print the lines in reverse order */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char **ptrs = 0;
size_t numptrs = 0;
size_t count = 0;
char *buffer = 0;
size_t buflen = 0;
while (getline(&buffer, &buflen, stdin) != -1)
{
if (count == numptrs)
{
size_t newnum = (numptrs + 2) * 2;
void *newptrs = realloc(ptrs, newnum * sizeof(*ptrs));
if (newptrs == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", newnum * sizeof(*ptrs));
exit(1);
}
ptrs = newptrs;
numptrs = newnum;
}
ptrs[count++] = strdup(buffer);
}
free(buffer);
/* Print lines in reverse order */
for (size_t i = count; i > 0; i--)
fputs(ptrs[i-1], stdout);
/* Free allocated memory */
for (size_t i = 0; i < count; i++)
free(ptrs[i]);
free(ptrs);
return 0;
}
很容易争辩说代码应该检查 strdup()
是否成功,如果不成功则采取适当的措施。释放已分配内存的代码应该在一个函数中——这也将使错误发生后的清理变得更容易。可以将代码修改为一个函数,该函数可用于处理列为命令行参数而不是标准输入的文件。
将此文本作为标准输入:
Because it messes up the order in which people normally read text.
> Why is top-posting such a bad thing?
>> Top-posting.
>>> What is the most annoying thing in e-mail?
程序产生输出:
>>> What is the most annoying thing in e-mail?
>> Top-posting.
> Why is top-posting such a bad thing?
Because it messes up the order in which people normally read text.
我有以下片段。我有 across 几个示例,其中可以使用 getline 读取一行,然后简单地打印它。我正在努力不断地从 stdin 保存,使用 getline() 读取并且可能有某种缓冲区来跟踪所有读取的内容(是否需要?)。
最后,我只是按照用户输入最后一行的倒序打印内容。 我不确定 char* 是否可以指向整个缓冲区 os stdin 输入,我们最终可以反向读取。
这是 运行 分段错误,我最好的猜测是访问从未分配的内存。
size_t linecount = 0;
ssize_t bytes_read;
size_t nbytes = 100;
char *content;
if (1) {
my_string = (char*) malloc(nbytes + 1);
while ((bytes_read = getline(&my_string, &nbytes, stdin)) >= 0
&& my_string[0] != '\n') {
puts(my_string);
printf("read: %ld bytes", bytes_read);
content = (char*) malloc((strlen(my_string) + 1) * sizeof(char));
success = content != NULL;
if (success) {
strcpy(content, my_string);
++linecount;
} else {
printf("Malloc error\n");
exit(1);
}
}
}
使用 getline()
读取,如果您提供初始化为 NULL
的指针并且大小参数初始化为零,那么 getline()
将为您的输入分配足够的存储空间——无论长度如何.如果您只是简单地反转字符串,则没有理由分配额外的存储空间。如果要在集合中保存由 getline()
读取的多行(例如使用指针数组,例如 char *lines[NLINES];
或指向指针的指针,例如 char **lines;
)
要使用 getline()
读取所有输入行,打开文件后(或简单地将 stdin
分配给 FILE*
指针),您需要不超过:
char *lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0; /* n set to 0, getline() allocates */
...
while (getline (&lineptr, &n, fp) != -1) /* read every line from file */
...
如果只是简单的将每一行的字符反转,可以写一个简单的字符串反转函数,输出反转后的字符串。 getline()
读取循环和一个名为 strrev()
的函数将字符串作为参数:
while (getline (&lineptr, &n, fp) != -1) /* read every line from file */
fputs (strrev (lineptr), stdout); /* reverse line & output */
制作你的 strrev()
函数来处理字符串,而不管 '\n'
是否存在允许你简单地传递来自 getline()
的包含 '\n'
字符的行最后,不必先 trim 字符串中的换行符。如果您确实想删除 '\n'
,您可以使用 getline()
返回的字符数,或者只需要简单地调用 strcspn()
,例如
while (getline (&lineptr, &n, fp) != -1) { /* read every line from file */
lineptr[strcspn (lineptr, "\n")] = 0; /* trim '\n' from end of lineptr */
/* do whatever else is desired */
}
把它放在一起,并将要读取的文件名作为程序的第一个参数(如果没有给出参数,则默认从 stdin
读取),你可以这样做:
#define _GNU_SOURCE /* getline() is POSIX not standard C */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strrev (char *str); /* function to reverse string in-place */
/* (str must be mutable, not a string-literal) */
int main (int argc, char **argv) {
char *lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0; /* n set to 0, getline() allocates */
/* 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 (getline (&lineptr, &n, fp) != -1) /* read every line from file */
fputs (strrev (lineptr), stdout); /* reverse line & output */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
free (lineptr); /* getline() allocates, so don't forget to free the memory */
}
/** strrev - reverse string, swaps src & dest each iteration up to '\n'.
* Takes valid string and reverses, original is not preserved.
* If str is valid, returns pointer to str, NULL otherwise.
*/
char *strrev (char *str)
{
if (!str) { /* validate str not NULL */
printf ("%s() error: invalid string\n", __func__);
return NULL;
}
if (!*str) /* check empty string */
return str;
char *begin = str,
*end = begin + strcspn (str, "\n") - 1;
while (end > begin)
{
char tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
return str;
}
例子Use/Output
从 stdin
读取并在完成后使用 Ctrl + d(Ctrl + z on windows) :
$ ./bin/getline_strrev
My dog has fleas
saelf sah god yM
My cat has none!
!enon sah tac yM
Lucky cat...
...tac ykcuL
从文件读取(或重定向)输入:
输入文件
$ cat dat/dog.txt
My dog has fleas
My cat has none!
Lucky cat...
例子Use/Output
$ ./bin/getline_strrev dat/dog.txt
saelf sah god yM
!enon sah tac yM
...tac ykcuL
重定向文件 stdin
:
$ ./bin/getline_strrev < dat/dog.txt
saelf sah god yM
!enon sah tac yM
...tac ykcuL
反转行而不是字符
根据您在评论中的说明,您可以通过分配指针(并根据需要重新分配)并为每行分配存储空间,分配块的地址来保存每行,从而存储未知长度的未知行数您的每个指针依次复制 getline()
读取的行到分配的块。您有两种类型的内存可以满足,内存块包含指针,内存块包含每一行。 How to read a text file and store in an array in C 一两天前对此进行了非常详细的解释。
该示例与此处的唯一区别是使用 getline()
而不是 fgets()
以及如何计算每行中的字符数并删除结尾 [=缓冲区中的 33=] 略有不同,只是由于 getline()
返回读取的字符数。颠倒行的顺序也不同(但也是微不足道的区别)
getline()
和反转行的变化是:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTRS 2 /* initial number of pointers to allocated */
int main (int argc, char **argv) {
char **lines = NULL, /* pointer to all lines */
*lineptr = NULL; /* pointer for getline() set NULL */
size_t n = 0, /* n set to 0, getline() allocates */
nptrs = NPTRS, /* no. of pointers to allocate */
used = 0; /* no. of pointers used */
ssize_t nchr; /* no. of chars read by getline() */
/* 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;
}
/* allocate initial nptrs pointers - validate EVERY allocation */
if ((lines = malloc (nptrs * sizeof *lines)) == NULL) {
perror ("malloc-lines");
return 1;
}
/* read every line from file, saving characters read in nchr */
while ((nchr = getline (&lineptr, &n, fp)) != -1) {
if (nchr > 0 && lineptr[nchr-1] == '\n') /* if chars read and \n */
lineptr[--nchr] = 0; /* trim \n from end */
if (used == nptrs) {
/* always realloc using a temporary pointer */
void *tmp = realloc (lines, (2 * nptrs) * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* don't exit, lines still good */
}
lines = tmp; /* assign reallocated block */
nptrs *= 2; /* update number of pointers */
}
/* allocate storage for line, assign address for new block to lines[used] */
if (!(lines[used] = malloc (nchr + 1))) {
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], lineptr, nchr + 1); /* copy line to new block of mem */
used += 1; /* increment used pointer counter */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
/* output lines in reverse order */
while (used--) {
puts (lines[used]);
free (lines[used]); /* free storage for each string when done */
}
free (lines); /* free pointers */
free (lineptr); /* free memory allocated by getline() */
}
例子Use/Output
与上述示例相同的 inpt 文件。
$ ./bin/getline_linerev dat/dog.txt
Lucky cat...
My cat has none!
My dog has fleas
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/getline_linerev dat/dog.txt
==6502== Memcheck, a memory error detector
==6502== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6502== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6502== Command: ./bin/getline_linerev dat/dog.txt
==6502==
Lucky cat...
My cat has none!
My dog has fleas
==6502==
==6502== HEAP SUMMARY:
==6502== in use at exit: 0 bytes in 0 blocks
==6502== total heap usage: 9 allocs, 9 frees, 5,887 bytes allocated
==6502==
==6502== All heap blocks were freed -- no leaks are possible
==6502==
==6502== For counts of detected and suppressed errors, rerun with: -v
==6502== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题或需要其他帮助,请告诉我。
由于目标是“以用户输入的相反顺序打印内容,最后一行在前”,程序必须存储所有行。 getline()
函数通常为每一行分配相当大的 space(在我的 Mac 上默认为 128 字节,如果输入行长于此则增长),因此通常最好有一个由 getline()
管理的缓冲区,如果需要可以增长,并以所需的长度将实际输入字符串复制到其他地方。我使用 strdup()
复制行。
/* Read file and print the lines in reverse order */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char **ptrs = 0;
size_t numptrs = 0;
size_t count = 0;
char *buffer = 0;
size_t buflen = 0;
while (getline(&buffer, &buflen, stdin) != -1)
{
if (count == numptrs)
{
size_t newnum = (numptrs + 2) * 2;
void *newptrs = realloc(ptrs, newnum * sizeof(*ptrs));
if (newptrs == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", newnum * sizeof(*ptrs));
exit(1);
}
ptrs = newptrs;
numptrs = newnum;
}
ptrs[count++] = strdup(buffer);
}
free(buffer);
/* Print lines in reverse order */
for (size_t i = count; i > 0; i--)
fputs(ptrs[i-1], stdout);
/* Free allocated memory */
for (size_t i = 0; i < count; i++)
free(ptrs[i]);
free(ptrs);
return 0;
}
很容易争辩说代码应该检查 strdup()
是否成功,如果不成功则采取适当的措施。释放已分配内存的代码应该在一个函数中——这也将使错误发生后的清理变得更容易。可以将代码修改为一个函数,该函数可用于处理列为命令行参数而不是标准输入的文件。
将此文本作为标准输入:
Because it messes up the order in which people normally read text.
> Why is top-posting such a bad thing?
>> Top-posting.
>>> What is the most annoying thing in e-mail?
程序产生输出:
>>> What is the most annoying thing in e-mail?
>> Top-posting.
> Why is top-posting such a bad thing?
Because it messes up the order in which people normally read text.