从没有最大缓冲区长度的C中的stdin读取

时间:2014-09-14 22:34:02

标签: c

以下代码设置从stdin读取的最大行大小。我不是硬编码特定的行长度,而是可以灵活地处理任何缓冲区长度。什么是允许处理任何规模的好策略?

如果这些策略复杂得多,是否有办法至少保证getline不会溢出?谢谢。

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

 #define P 20

 int main()
 {
   size_t size = 1920;
   char *line;
   // record row; /* structure to store fields */
   char tokens[P][41];
   int p;
   char delims[] = ",";     /* ", |" */
   char *result = NULL;

   line = ( char * ) malloc( size + 1 );

   while( getline(&line, &size, stdin) != -1 )
   {
      /* chomp */
      line[strlen(line)-1] = '\0';

      /* load char array */
      result = strtok( line , delims );
      p = 0;
      while( result != NULL && ( p < P ) ) 
      {
         strcpy( tokens[p++] , result );
         result = strtok( NULL, delims );
      }

      if (p != P)
      {
         fprintf(stderr,"Wrong number of input fields.\nFormat: ID,x1, ... ,x%d\n",P);
     exit(-1);
      }

      /* load record ( atol, atof etc... , skipped for brevity ) and work with record */

      return 0;
 }

2 个答案:

答案 0 :(得分:7)

您可以getline为您分配内存(这是使用non-standard getline function over the standard fgets function的全部要点)。从getline手册页:

  

如果*lineptrNULL,则getline()将分配缓冲区进行存储   该行,应该由用户程序释放。 (*n中的值   被忽略了。)

     

或者,在调用getline()之前,*lineptr可以包含指针   到malloc - 分配的缓冲区*n字节大小。如果缓冲区不是   大到足以保持该行,getline()使用realloc调整其大小,   根据需要更新*lineptr*n

所以你可以这样做:

line = NULL;
while (getline(&line, &size, stdin))
{
    // ... Do stuff with `line`...
}
free(line);

(或者按原样保留您的代码,因为getline会为您调整分配的缓冲区大小。)

答案 1 :(得分:2)

这是我一直在使用的代码--Fgetstr(FILE *,const char *)。它大约使每个realloc的缓冲区大小加倍,并且不会在失败的malloc / realloc上崩溃。调用如:char * text = Fgetstr(stdin,“\ n”);或者其他什么。

库getdelim()函数类似,虽然我的可能会更老。 getline和getdelim上的联机帮助页没有详细说明如果malloc和realloc在我的系统上失败会发生什么,并且只提到可能的错误EINVAL(没有ENOMEM)。因此,面对内存耗尽的行为可能未定义getline / getdelim。

另外,正如starrify指出的那样,很多系统都没有拥有 getline。

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>

#ifdef TEST
#define DEBUG
#endif

#ifdef DEBUG
#undef DEBUG
#define DEBUG(b) {b}
#else
#define DEBUG(b)  
#endif

#ifdef TEST
int main (int argc, char **argv)
{
    char *text = (char*)0;
    char *ends = "\n";

    if(argc > 1) ends = argv[1];

    while(text = Fgetstr(stdin, ends))
    {
        puts(text);
        free(text);
    }

    return 0;
}
#endif

/*  return specifications -
 *
 *  terminators include : ends, \0, and EOF
 *
 *  root    EOF?    text?   ended?  stat    returned value
 *          -       -       -       ... 
 *  1       -       -       1       return  ""
 *          -       1       -       ... 
 *  2       -       1       1       return  "text"
 *  3       1       -       -       return  -null-      EOF-*accepted*
 *  4       1       -       1       return  ""          EOF-postponed
 *  5       1       1       -       return  "text"      EOF-postponed/fake-end
 *  6       1       1       1       return  "text"      EOF-postponed/true-end
 *
 *  on ENOMEM, return -null-
 *
 */

static char *Fgetstr_R(FILE *ifp, const char *ends, unsigned int offset)
{
    char *s = (char*)0;                     /* the crucial string to return */
    unsigned int bufmax = offset;           /* as large as so far */
    unsigned int bufidx = 0;                /* index within buffer */
    char buffer[bufmax + 1];                /* on-stack allocation required */
    int ended = 0;                          /* end character seen ? */
    int eof = 0;                            /* e-o-f seen ? */

    DEBUG(fprintf(stderr, "(%d", offset););

    while(bufidx <= bufmax)     /* pre-recurse - attempt to fill buffer */
    {
        int c = getc(ifp);

        if( (ended = ( !c || (ends && strchr(ends,c)))) || (eof = (EOF==c)) )  
            break;

        buffer[bufidx++] = (char)c;
    }

    /* note - the buffer *must* at least have room for the terminal \0 */

    if(ended || (eof && offset))                    /* root 1,2,4,6 5 */
    {
        unsigned int offset_max = offset + bufidx;
        DEBUG(fprintf(stderr, " malloc %d", offset_max + 1););
        if(s = (char*)malloc((offset_max + 1) * sizeof(char)))
            s[offset_max] = '\0';
        else
            s = (char*)0, perror("Fgetstr_R - malloc");
    }
    else
    {
        if(eof && !offset)  /* && !ended */     /* root 3 */
            s = (char*)0;
        else
            s = Fgetstr_R(ifp, ends, offset + bufidx);  /* recurse */
    }

    /* post-recurse */

    if(s)
        strncpy(&s[offset], &buffer[0], bufidx);  /* cnv. idx to count */

    DEBUG(fprintf(stderr, ")", offset););
    return s;
}

char *Fgetstr (FILE *ifp, const char *ends)
{
    register char *s = (char*)0;
    DEBUG(fprintf(stderr, "Fgetstr "););
    s = Fgetstr_R(ifp, ends, 0);
    DEBUG(fprintf(stderr, ".\n"););
    return s;
}