简体   繁体   English

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

[英]Read from stdin in C without max buffer length

The following code sets a maximum line size read from stdin. 以下代码设置从stdin读取的最大行大小。 I'd rather not hard-code a specific line length, and have the flexibility to handle any buffer length. 我宁愿不对特定的行长度进行硬编码,并且可以灵活地处理任何缓冲区长度。 What are good strategies to allow processing of any size? 什么是允许处理任何尺寸的好策略?

If these strategies are much more complex, is there a way to at least guarantee that getline will not overflow? 如果这些策略复杂得多,有没有办法至少保证getline不会溢出? Thanks. 谢谢。

 #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;
 }

You can have getline allocate memory for you (which is the whole point of using the non-standard getline function over the standard fgets function ). 你可以让getline为你分配内存(这是使用非标准getline函数而不是标准fgets函数的全部要点)。 From the getline manual page: 从获取getline手册页:

If *lineptr is NULL , then getline() will allocate a buffer for storing the line, which should be freed by the user program. 如果*lineptrNULL ,则getline()将分配用于存储该行的缓冲区,该缓冲区应由用户程序释放。 (The value in *n is ignored.) *n的值被忽略。)

Alternatively, before calling getline() , *lineptr can contain a pointer to a malloc -allocated buffer *n bytes in size. 或者,在调用getline()之前, *lineptr可以包含指向malloc分配的缓冲区*n字节大小的指针。 If the buffer is not large enough to hold the line, getline() resizes it with realloc , updating *lineptr and *n as necessary. 如果缓冲区不足以容纳该行,则getline()使用realloc调整其大小,根据需要更新*lineptr*n

So you can do: 所以你可以这样做:

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

(Or leave your code as-is, since getline will resize your allocated buffer for you.) (或保持代码原样,因为getline会为您调整分配的缓冲区大小。)

Here's the code I've been using - Fgetstr(FILE*, const char*). 这是我一直在使用的代码--Fgetstr(FILE *,const char *)。 It roughly doubles the buffer size for each realloc, and won't crash on a failed malloc/realloc. 它大约使每个realloc的缓冲区大小加倍,并且不会在失败的malloc / realloc上崩溃。 Called like: char *text = Fgetstr(stdin, "\\n"); 调用如:char * text = Fgetstr(stdin,“\\ n”); or whatever. 管他呢。

The library getdelim() function is similar, although mine might be much older. 库getdelim()函数类似,虽然我的可能更老。 The manpage on getline and getdelim doesn't detail what happens if the malloc and realloc fail on my system, and only mention a possible error EINVAL (no ENOMEM). getline和getdelim上的联机帮助页没有详细说明如果malloc和realloc在我的系统上失败会发生什么,并且只提到可能的错误EINVAL(没有ENOMEM)。 Hence, the behavior in the face of memory exhaustion may be undefined for getline/getdelim. 因此,对于getline / getdelim,面对内存耗尽的行为可能是未定义的。

Also, as starrify points out, many systems don't have getline. 此外,作为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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM