【问题标题】:Read unknown number of lines from stdin, C从标准输入读取未知数量的行,C
【发布时间】:2014-10-23 21:18:29
【问题描述】:

我在读取未知大小的标准输入时遇到问题。实际上它是 .txt 文件中的一个表,我通过调用参数'

#include <stdio.h>
#include <string.h>


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

   char words[10][1024];
   int i=0;
   while(feof(stdin)==0)
   {
      fgets(words[i],100,stdin); 
      printf("%s", words[i]);
      i++;
   }
   return 0;
}

但问题是我不知道行数,在这种情况下是 10(我们知道行中的字符数 - 1024)。 如果有人知道解决方案,那就太好了。提前致谢。

【问题讨论】:

  • 1) 使用 feof(stdin) 是错误的。请改用fgets() 形式的返回值。 2) 可能需要 1024+2,'\n' 需要 1 个,'\0' 需要 1 个。
  • while((fgets(words,1024,stdin))!=0) 它可以工作,谢谢,顺便说一句 '\n' 应该包含在这些 1024 中
  • 如果'\n'是行的第1024个char,words应该是char words[1024+1];

标签: c stdin fgets


【解决方案1】:

您遇到了困扰所有新 C 程序员的问题之一。如何动态分配我需要的所有内存,使自己摆脱静态限制,同时仍然跟踪我在内存中收集的“东西”。当您需要从输入中读取未知数量的“事物”时,通常会出现此问题。初始选项是 (1) 声明一些足够大的限制以工作(违背目的),或 (2) 根据需要动态分配指针。

显然,目标是(2)。但是,您会遇到“我如何跟踪分配的内容?”的问题,这本身就是一个困扰初学者的问题。问题是,如果我使用一堆指针动态分配,**如何遍历列表以取出我的“东西”?此外,您必须初始化一些初始数量的指针(除非使用像链表这样的高级数据结构),所以下一个问题是“当我用完时该怎么办?”

通常的解决方案是分配一组初始指针,然后当达到限制时,重新分配为原始指针的两倍,然后继续。 (正如格雷森在他的回答中指出的那样)。

但是,还有一个值得了解的技巧遍历列表以恢复您的“东西”。是的,您可以使用malloc 进行分配并跟踪使用的指针数量,但是您可以通过最初使用calloc 进行分配来将计数器 绑定到您的指针列表中。这不仅分配空间,还将分配的指针设置为NULL(或0)。这允许您使用简单的while (pointer != NULL) 遍历您的列表。这在将指针集合传递给函数等方面提供了许多好处。缺点(最小的一个)是您可以编写一个使用calloc 分配新的重新分配方案需要时的空间。 (可惜,我变得更聪明了——但我必须努力做到这一点……)

您可以根据您的要求评估是否使用现成的malloc/realloc,或者是否使用calloc 和custom reallocate function 重新分配。无论如何,了解两者,只是为您的编程工具箱添加更多工具。

好吧,废话不多说,这些废话的例子在哪里?

以下两个示例都只是从任何文本文件中读取所有行并将这些行(带有指针索引号)打印回标准输出。两者都希望您提供要读取的文件名作为命令行上的第一个参数。两者之间的唯一区别是第二个使用calloc 完成的重新分配是custom reallocation function。它们最初都分配255 指针,并且每次达到限制时指针数量加倍。 (为了好玩,您可以将MAXLINES 设置为10 之类的小东西,并强制重复重新分配以进行测试)。

第一个在 main() 中重新分配的示例

# include <stdio.h>
# include <stdlib.h>
# include <string.h>

#define MAXLINES 255

void free_buffer (char **buffer)
{
    register int i = 0;

    while (buffer[i])
    {
        free (buffer[i]);
        i++;
    }

    free (buffer);

}

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

    if (argc < 2) {
        fprintf (stderr, "Error: insufficient input. Usage: %s input_file\n", argv[0]);
        return 1;
    }

    char *line = NULL;      /* forces getline to allocate space for buf */
    ssize_t read = 0;       /* number of characters read by getline     */
    size_t n = 0;           /* limit number of chars to 'n', 0 no limit */
    char **filebuf = NULL;
    char **rtmp = NULL;
    int linecnt = 0;
    size_t limit = MAXLINES;
    size_t newlim = 0;

    FILE *ifp = fopen(argv[1],"r");
    if (!ifp)
    {
        fprintf(stderr, "\nerror: failed to open file: '%s'\n\n", argv[1]);
        return 1;
    }

    filebuf = calloc (MAXLINES, sizeof (*filebuf)); /* allocate MAXLINES pointers           */

    while ((read = getline (&line, &n, ifp)) != -1) /* read each line in file with getline  */
    {
        if (line[read - 1] == 0xa) { line[read - 1] = 0; read--; } /* strip newline         */

        if (linecnt >= (limit - 1))                 /* test if linecnt at limit, reallocate */
        {
            newlim = limit * 2;                     /* set new number of pointers to 2X old */
            if ((rtmp = calloc (newlim, sizeof (*filebuf))))    /* calloc to set to NULL    */
            {
                /* copy original filebuf to newly allocated rtmp */
                if (memcpy (rtmp, filebuf, linecnt * sizeof (*filebuf)) == rtmp)
                {
                    free (filebuf);                 /* free original filebuf                */
                    filebuf = rtmp;                 /* set filebuf equal to new rtmp        */
                }
                else
                {
                    fprintf (stderr, "error: memcpy failed, exiting\n");
                    return 1;
                }
            }
            else
            {
                fprintf (stderr, "error: rtmp allocation failed, exiting\n");
                return 1;
            }
            limit = newlim;                         /* update limit to new limit    */
        }

        filebuf[linecnt] = strdup (line);           /* copy line (strdup allocates) */

        linecnt++;                                  /* increment linecnt            */
    }

    fclose(ifp);

    if (line) free (line);                          /* free memory allocated to line    */

    linecnt = 0;                                    /* reset linecnt to iterate filebuf */

    printf ("\nLines read in filebuf buffer:\n\n"); /* output all lines read            */
    while (filebuf[linecnt])
    {
        printf (" line[%d]: %s\n", linecnt, filebuf[linecnt]);
        linecnt++;
    }

    printf ("\n");

    free_buffer (filebuf);                          /* free memory allocated to filebuf */

    return 0;
}

在自定义函数中重新分配的第二个示例

# include <stdio.h>
# include <stdlib.h>
# include <string.h>

#define MAXLINES 255

/* function to free allocated memory */
void free_buffer (char **buffer)
{
    register int i = 0;

    while (buffer[i]) 
    { 
        free (buffer[i]); 
        i++; 
    }

    free (buffer);

}

/* custom realloc using calloc/memcpy */
char **recalloc (size_t *lim, char **buf)
{
    int newlim = *lim * 2;
    char **tmp = NULL;

    if ((tmp = calloc (newlim, sizeof (*buf))))
    {
        if (memcpy (tmp, buf, *lim * sizeof (*buf)) == tmp)
        {
            free (buf);
            buf = tmp;
        }
        else
        {
            fprintf (stderr, "%s(): error, memcpy failed, exiting\n", __func__);
            return NULL;
        }
    }
    else
    {
        fprintf (stderr, "%s(): error, tmp allocation failed, exiting\n", __func__);
        return NULL;
    }

    *lim = newlim;

    return tmp;
}

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

    if (argc < 2) {
        fprintf (stderr, "Error: insufficient input. Usage: %s input_file\n", argv[0]);
        return 1;
    }

    char *line = NULL;      /* forces getline to allocate space for buf */
    ssize_t read = 0;       /* number of characters read by getline     */
    size_t n = 0;           /* limit number of chars to 'n', 0 no limit */
    char **filebuf = NULL;
    int linecnt = 0;
    size_t limit = MAXLINES;

    FILE *ifp = fopen(argv[1],"r");
    if (!ifp)
    {
        fprintf(stderr, "\nerror: failed to open file: '%s'\n\n", argv[1]);
        return 1;
    }

    filebuf = calloc (MAXLINES, sizeof (*filebuf)); /* allocate MAXLINES pointers           */

    while ((read = getline (&line, &n, ifp)) != -1) /* read each line in file with getline  */
    {
        if (line[read - 1] == 0xa) { line[read - 1] = 0; read--; } /* strip newline         */

        if (linecnt >= (limit - 1))                 /* test if linecnt at limit, reallocate */
        {
            filebuf = recalloc (&limit, filebuf);   /* reallocate filebuf to 2X size        */
            if (!filebuf)
            {
                fprintf (stderr, "error: recalloc failed, exiting.\n");
                return 1;
            }
        }

        filebuf[linecnt] = strdup (line);           /* copy line (strdup allocates)     */

        linecnt++;                                  /* increment linecnt                */
    }

    fclose(ifp);

    if (line) free (line);                          /* free memory allocated to line    */

    linecnt = 0;                                    /* reset linecnt to iterate filebuf */

    printf ("\nLines read in filebuf buffer:\n\n"); /* output all lines read            */
    while (filebuf[linecnt])
    {
        printf (" line[%d]: %s\n", linecnt, filebuf[linecnt]);
        linecnt++;
    }

    printf ("\n");

    free_buffer (filebuf);                          /* free memory allocated to filebuf */

    return 0;
}

看看这两个例子。知道有很多很多方法可以做到这一点。这些示例只是提供了一种方法,该方法提供了使用一些您通常会发现的额外技巧的示例。试一试。如果您需要更多帮助,请发表评论。

【讨论】:

  • 感谢您的详细说明!
  • Curious OP 选择了这个作为答案,但评论 here 声明“不得使用 malloc、realloc 或 calloc”。
  • 你让我不知所措,我以前没见过那个评论。在这种方法中,我当然使用calloc 进行主要分配和重新分配。至于为什么?谁知道呢,说不定是有误会后来才解开的?
【解决方案2】:

我建议你使用malloc 和realloc 来管理你的内存。跟踪您的数组有多大或有多少条目,并在数组不够大时调用realloc 将其大小加倍。

【讨论】:

  • 我忘了说我不能使用malloc、realloc或calloc,我应该读一行,保存我需要的数据,然后再读另一行
  • 现在还不清楚是什么问题。听起来您不允许将所有行存储在内存中,所以不要这样做。您可以拥有一个存储一行的数组,然后将每一行读入其中,覆盖之前的任何内容。你在寻找什么样的答案?你想做什么?这是作业吗?
  • 啊,是的,现在我明白了,我不需要知道它有多长,顺便说一句,这是家庭作业:int main(int argc,char *argv[]) { char words[1024]; while((fgets(words,1024,stdin))!=0) { printf("%s", words); } return 0; }
【解决方案3】:

Op 似乎需要将数据存储在某处

#define N 100000u
char BABuffer[N];

int main(int argc, char *argv[]) {
  size_t lcount = 0;
  size_t ccount = 0;
  char words[1024 + 2];

  while(fgets(words, sizeof words, stdin) != NULL) {
    size_t len = strlen(words);
    if (ccount + len >= N - 1) {
      fputs("Too much!\n", stderr);
      break;
    }
    memcpy(&BABuffer[ccount], words, len);
    ccount += len;
    lcount++;
  }
  BABuffer[ccount] = '\0';

  printf("Read %zu lines.\n", lcount);
  printf("Read %zu char.\n", ccount);
  fputs(BABuffer, stdout);
  return 0;
}

注意:ccount 包括行尾字符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    相关资源
    最近更新 更多