Rcjp's Weblog

December 19, 2006

Reading in Files

Filed under: c, lisp — rcjp @ 5:52 pm

I was catching up on Dr.Dobb’s columns and came across Walter Browns clean way of reading lines from files in Andrew Koenig’s c++ blog.

    #include <iostream>    // for cout 
    #include <fstream>     // for ifstream 
    #include <string>      // for string 

    int main()
    {
        std::ifstream file("t2.cpp");

        for(std::string s; getline(file, s);)
        {
            std::cout << s << '\n';
        }
    }


which is a nice alternative to the usual while loop as the string is scoped in the for loop that uses it.

Of course you can do something similar in C, provided you use -std=c99 compiler flag to gcc to allow declarations in for loops…

    #include <stdio.h>
    #define LINE_MAX 256

    int main()
    {
        FILE *fp=fopen("t1.c","r");

        for (char line[LINE_MAX]; fgets(line, LINE_MAX, fp);)
        {
            printf("line = %s\n", line);
        }
    }


Production code should always check the results of fopen, and fgets may return a NULL because of a problem reading the file – not because it reached the end, so you should check with feof. But in ‘quick and dirty’ mode I skip these and C will segmentation fault trying to read from a missing file. C++ will just quietly carry on, neither of which is ideal.

Common Lisp will throw you into the debugger when

    (defun test ()
      (with-open-file (stream "badfilename" :direction :input)
        (loop for line = (read-line stream nil nil)
           while line do
             (format t "line = ~A~%" line))))


fails to find the file. (I’ve never really liked those nil’s that follow read-line though, the first says the end of file isn’t an error and the second is what you want to signify the end of input. I think I’d prefer it if they were keyword arguments – more descriptive.)

If you are using a lisp compiler that supports restarts (unfortunately CMUCL/SBCL don’t as far as I can tell, but clisp and Lispworks do) you can quickly patch the running code if you don’t feel like going back to the source just yet…

    r@laptop:~/lisp/tmp$ clisp -q
    [1]> (load "test.lisp")
    ;; Loading file test.lisp ...
    ;; Loaded file test.lisp
    T
    [2]> (test)

    *** - OPEN: file #P"/home/r/tmp/badfilename" does not exist
    The following restarts are available:
    ABORT          :R1      ABORT
    Break 1 [3]>


first find out where you are with :w

    Break 1 [3]> :w
    <1> #<SYSTEM-FUNCTION OPEN>
    EVAL frame for form (OPEN "badfilename" :DIRECTION :INPUT)
    Break 1 [3]>


now you can see the actual open command the with-open-file macro got expanded into, and use :rt to return the correct filename by typing in the correct value of OPEN at the “Values:” prompt and carry on execution

    Break 1 [3]> :rt
    Values: (OPEN "correctfilename" :DIRECTION :INPUT)


There is an interesting document showing some more advanced restarting of code here using acl but is worth reading even if you don’t have that implementation.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: