Thread: Reading indeterminate length line from file

  1. #1
    Registered User
    Join Date
    Sep 2003
    Posts
    224

    Reading indeterminate length line from file

    Is there a way to read a line of an indeterminate size in C without setting an initial size of the string or resizing the string? fgets seems to do the job, but you still have to specify a size. Is there a work around? Without appending strings and doing other inefficient stuff?

    Thanks,
    Yasir

  2. #2
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    You could do something like this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
      char *str = NULL;
      int ch, len = 0;
    
      printf("Enter some arbitrarily long string: ");
      fflush(stdout);
      while((ch = getchar()) != '\n' && ch != EOF)
      {
        if(!(str = realloc(str, len+1)))
        {
          puts("Memory allocation error!");
          return 1;
        }
        str[len++] = ch;
      }
      str[len] = '\0';
    
      return 0;
    }
    Of course you can make it more efficient by allocating memory in chunks instead of for each character, and you can do some other things if you get creative. But other than something like the above, no, you can't.
    If you understand what you're doing, you're not learning anything.

  3. #3
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Does realloc resize an object, that is make another buffer and then copy the old contents, or does it somehow just extend the array?
    Last edited by Yasir_Malik; 12-09-2004 at 03:16 PM.

  4. #4
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    It resizes the array. The memory it allocates is contiguous so if it can't extend without running into other memory that's already in use it will relocate the chunk of memory you're using to somewhere big enough. But it's guaranteed to keep the contents of the relocated memory intact so the relocation should be transparent to you.

    It's possible to run into problem if you do something like this:
    Code:
    {
      void *ptr, *ptr2;
    
      ptr = malloc(10);
      ptr2 = ptr+5;
      ptr = realloc(ptr, 20);
    }
    If the realloc() call relocates the reserved memory then ptr2 is pointing to memory that doesn't really belong to the program anymore and dereferencing ptr2 is "undefined". This seems to be a common pitfall with realloc() usage.
    If you understand what you're doing, you're not learning anything.

  5. #5
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Ok. Thanks.
    Does using realloc() in this case any more efficient than using multiple loops to read the line and using strcat() to piece everything up?

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > Does using realloc() in this case any more efficient than using multiple loops
    Nope - it's horribly inefficient compared to some alternatives....

  7. #7
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    ... such as simply using a while loop and strcat()?

  8. #8
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    The bottom line is that you need a place to store the data. And the only way (besides using an intermediary) is through dynamic memory allocation. realloc() is going to have to be involved in some way unless you put a cap on the size of the input. If you want to create a buffer that can hold an arbitrarily long string you're going to need to allocate the memory a bit at a time. Do you see where I'm going with this?
    Last edited by itsme86; 12-09-2004 at 03:55 PM.
    If you understand what you're doing, you're not learning anything.

  9. #9
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    [edit]This is a construct to avoid -- follow the above link.
    Last edited by Dave_Sinkula; 12-09-2004 at 04:16 PM.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  10. #10
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    Quote Originally Posted by Dave_Sinkula
    Hmm? THat's what I wrote

    EDIT: Ahh...I hadn't realized that was a link

    I know it's important not to do that if you want to use the data after an allocation failure, but...I didn't care about the data
    Last edited by itsme86; 12-09-2004 at 04:31 PM.
    If you understand what you're doing, you're not learning anything.

  11. #11
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >but...I didn't care about the data
    I care about the data. How are you going to rationalize it if I use your code and get burned?
    My best code is written with the delete key.

  12. #12
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Code:
    char *getaline( int size )
    {
        char *s;
        int c = fgetc( stdin );
        if( c == '\n' )
        {
            s = malloc( size + 1 );
            memset( s, 0, size +1 );
        }
        else
        {
            s = getaline( size + 1 );
            s[size] = c;
        }
        return s;
    }
    Yeah, that looks about right. Called with an initial size of 0.

    Quzah.
    Hope is the first step on the road to disappointment.

  13. #13
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    Wow, if you don't mind the incredible inefficiency.

    I guess my statement about needing realloc() was mistaken nonetheless

    Why the memset() though? Wouldn't s[size+1] = 0 work just as well since the preceding characters will all get filled with input?

    Very clever use of the stack for temporary input storage
    Last edited by itsme86; 12-09-2004 at 09:30 PM. Reason: Need to spread reputation around. Bah!
    If you understand what you're doing, you're not learning anything.

  14. #14
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by itsme86
    Wow, if you don't mind the incredible inefficiency.

    I guess my statement about needing realloc() was mistaken nonetheless

    Why the memset() though? Wouldn't s[size+1] = 0 work just as well since the preceding characters will all get filled with input?

    Very clever use of the stack for temporary input storage
    How is that any more inefficient then calling realloc over and over? I dare say it's more efficient. Yeah, memset isn't needed, but it would actually be:
    Code:
    s[size] = '\0';
    Not "size +1".

    Illustrated for the unknowing:
    Code:
    foo( 0 )
    {
        c = 'b', size = 0
        foo( 1 )
        {
            c = 'a', size = 1
            foo( 2 )
            {
                c = 'r', size = 2
                foo( 3 )
                {
                    c = newline, size = 3
                    s = malloc( 4 )
                    s[3] = null
                }
                s[2] = c;
            }
            s[1] = c;
        }
        s[0] = c;
    }
    So yeah, if we kill the memset call and replace it with a null assignment that works better.

    [edit]
    To clarify, we'd have to define "efficient" and the frame of reference. It would probably be deemed less efficient in terms of memory. However, in terms of speed it should win out hands down.
    [/edit]


    Quzah.
    Last edited by quzah; 12-09-2004 at 10:56 PM.
    Hope is the first step on the road to disappointment.

  15. #15
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    or just read into a buffer that's filesize+1 using fgets, query the length, then size the user buffer accordingly.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 7
    Last Post: 02-02-2009, 07:27 AM
  2. help again with scrolling without wrapping
    By Dukefrukem in forum C Programming
    Replies: 8
    Last Post: 09-21-2007, 12:48 PM
  3. C++ std routines
    By siavoshkc in forum C++ Programming
    Replies: 33
    Last Post: 07-28-2006, 12:13 AM
  4. Unknown Memory Leak in Init() Function
    By CodeHacker in forum Windows Programming
    Replies: 3
    Last Post: 07-09-2004, 09:54 AM
  5. Greenhand want help!
    By leereg in forum C Programming
    Replies: 6
    Last Post: 01-29-2002, 06:04 AM