7

There are a bunch of ways describing how to use various methods to print out lines of a text file on this site:

They all seem to be tailored to a specific example.

It would be great to have the Clearest and Most Concise and Easiest way to simply: print each line of any text file to the screen. Preferably with detailed explanations of what each line does.

Points for brevity and clarity.

Community
  • 1
  • 1
Dlinet
  • 1,093
  • 3
  • 14
  • 22
  • What are you trying to do ? Do you want to dump the whole file ? – benjarobin Dec 28 '12 at 00:04
  • On UNIX, it's called `cat`. – Linuxios Dec 28 '12 at 00:05
  • @benjarobin I would like to print each individual line of the text file to the screen. If the file had say 5 lines, I would like the clearest way to simply open it and print those five lines. As far as my actual code; I don't have a specific use in mind. It would just be great to have an excellent grasp of the best way to execute this simple concept. – Dlinet Dec 28 '12 at 00:08

4 Answers4

9
#include <stdio.h>

static void cat(FILE *fp)
{
    char   buffer[4096];
    size_t nbytes;
    while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), fp)) != 0)
         fwrite(buffer, sizeof(char), nbytes, stdout);
}

int main(int argc, char **argv)
{
    FILE *fp;
    const char *file;
    while ((file = *++argv) != 0)
    {
        if ((fp = fopen(file, "r")) != 0)
        {
            cat(fp);
            fclose(fp);
        }
    }
    return(0);
}

The cat() function is not strictly necessary, but I'd rather use it. The main program steps through each command line argument and opens the named file. If it succeeds, it calls the cat() function to print its contents. Since the call to fopen() does not specify "rb", it is opened as a text file. If the file is not opened, this code silently ignores the issue. If no files are specified, nothing is printed at all.

The cat() function simply reads blocks of text up to 4096 bytes at a time, and writes them to standard output ('the screen'). It stops when there's no more to read.

If you want to extend the code to read standard input when no file is specified, then you can use:

if (argc == 1)
    cat(stdin);
else
{
    ...while loop as now...
}

which is one of the reasons for having the cat() function written as shown.

This code does not pay direct attention to newlines — or lines of any sort. If you want to process it formally one line at a time, then you can do several things:

static void cat(FILE *fp)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), fp) != 0)
         fputs(buffer, stdout);
}

This will read and write one line at a time. If any line is longer than 4095 bytes, it will read the line in two or more operations and write it in the same number of operations. Note that this assumes a text file in a way that the version using fread() and fwrite() does not. On POSIX systems, the version with fread() and fwrite() will handle arbitrary binary files with null bytes ('\0') in the data, but the version using fgets() and fputs() will not. Both the versions so far are strictly standard C (any version of the standard) as they don't use any platform-specific extensions; they are about as portable as code can be.

Alternatively again, if you have the POSIX 2008 getline() function, you can use that, but you need #include <stdlib.h> too (because you end up having to release the memory it allocates):

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    while (getline(&buffer, &buflen, fp) != -1)
         fputs(buffer, stdout);
    free(buffer);
}

This version, too, will not handle binary data (meaning data with null bytes in it). It could be upgraded to do so, of course:

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    ssize_t nbytes;
    while ((nbytes = getline(&buffer, &buflen, fp)) != -1)
         fwrite(buffer, sizeof(char), nbytes, stdout);
    free(buffer);
}

The getline() function reports how many bytes it read (there's a null byte after that), but the fwrite() function is the only one that takes a stream of arbitrary bytes and writes them all to the given stream.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • Why the fixed size buffer? A malloc wouldn't be hard with a stat call, and much more secure and portable, not to mention better. – Linuxios Dec 28 '12 at 00:10
  • More secure ? More portable ? Why ? Slower yes, remember that everything on the stack is most likely in the CPU cache... Why using the HEAP (a malloc is not slow, but it is not fast) ? – benjarobin Dec 28 '12 at 00:12
  • a malloc() would cause extra conditions. Also: the program is I/O bound anyway, and read_bunch + write_bunch is disastrous for the disk-LRU. – wildplasser Dec 28 '12 at 00:12
  • @Jonathan Leffler In python, to print the lines in a file to the screen is 4 lines. I know c is much more complex, but I was hoping that there would be a solution less than six times as long... – Dlinet Dec 28 '12 at 00:18
  • @Linuxios: A fixed size buffer is simpler than `malloc()`. Using `stat()` ties you to POSIX systems; the original code is portable to any system supporting (any) standard C version. Simplicity was one of the goals. – Jonathan Leffler Dec 28 '12 at 00:19
  • +1 for getting there anyway, although this limits file size. @Dlinet: I don't think there is a such thing as a simple C program that actually does anything more then print 2+2. – Linuxios Dec 28 '12 at 00:20
  • @Dlinet: There is very little flab in the code. In strict C99 or C2011, I could drop the `return(0);` in `main()`. If I was willing to use K&R style indentation (I'm not), I could lose a couple of lines. Strictly, the braces after the while loop are not necessary, but I'd not omit them either. Be my guest and compress it further. I skimped on error reporting on failure to open the file. – Jonathan Leffler Dec 28 '12 at 00:25
  • @Linuxios: there is nothing I see in the code that limits the size of file that can be copied, in any of the 3 or 4 versions shown. Can you explain where you think there's a limit? – Jonathan Leffler Dec 28 '12 at 00:28
  • @JonathanLeffler Firstly, thank you for you help with this problem. I guess I was hoping that there might surface an example similar (in simplicity) to the third link I posted, but able to process any file as opposed to one with known contents. – Dlinet Dec 28 '12 at 00:30
  • @JonathanLeffler: Misread. I thought you were using a fixed size buffer for the entire file. – Linuxios Dec 28 '12 at 00:32
  • @Dlinet: I'd rather not dissect the third link; the code in the question is gruesome (and the kids are saturating my internet connection, bother it!). The answer isn't much more help; it is all very much tied to a specific file format, unlike the code above. There is nothing in the code above that knows about the contents of the file except to the extent that it expects a text file. On Unix, the version using `fread()` and `fwrite()` will work with arbitrary binary files too; the other versions assume strings without embedded null bytes `'\0'`. The code really makes very few assumptions. – Jonathan Leffler Dec 28 '12 at 00:37
  • @JonathanLeffler: fair enough! I'll wait a bit longer in holding out for a miracle and if none comes I will mark your answer as accepted tomorrow and work on understanding it the best I can :) – Dlinet Dec 28 '12 at 00:44
  • 2
    @Dlinet, this is really excellent C code. Python can solve this in fewer lines, but that is because Python is a higher-level language than C. For simple problems like this, the advantage is with Python; but C gives the experienced developer complete control over what gets done and how it gets done. Python can solve this in fewer lines... but Python is written in C! (Well, CPython is anyway. There are other flavors of Python. But CPython came first and is still king.) – steveha Dec 28 '12 at 00:54
  • @JonathanLeffler If you are interested in providing me with a bit more help, I added an "answer" to my own question that works and is very short. I would love some advice as to the problems with it! – Dlinet Jan 02 '13 at 23:58
2

Well, here is a very short solution I eventually made. I imagine there is somethign fundamentally wrong with it otherwise it would have been suggested, but I figured I would post it here and hope someone tears it apart:

#include <stdio.h>
main()
{
    FILE *MyFile;
    int c;
    MyFile=fopen("C:\YourFile.txt","r");
    c = fgetc(MyFile);
    while (c!=EOF)
    {
        printf("%c",c);
        c = fgetc(MyFile);
    }
}
Dlinet
  • 1,093
  • 3
  • 14
  • 22
  • 2
    The questionable things are: (1) this is printing characters, not lines, as asked for by the question; (2) you don't check that the file is opened successfully before using the return value from `fopen()`; (3) your declaration for `main()` is not valid in C99 or C2011 — it should be `int main(void)` or `int main(int argc, char **argv)` (or, at a pinch, `int main()`); (4) it might be better to use `putchar()` or `fputc()` than to use `printf()` to output a single character; (5) it would be more idiomatic C to use `while ((c = fgetc(MyFile)) != EOF) putchar(c);`; (6) missing `fclose(MyFile)`. – Jonathan Leffler Jan 03 '13 at 00:47
  • 1
    +1 for using `int c;` — that is both correct and important. Strictly, since you wrote a C89 definition for `main()`, you should use `return(0);` (give or take the parentheses) at the end of `main()`. If your code was C99 compliant, you could get away without that, though in my view it is better to include it even in C99 or C2011 code. – Jonathan Leffler Jan 03 '13 at 00:49
  • @JonathanLeffler Thank You Jonathan, That was very insightful. I will look up more about the different types/years of C and their main() declarations! – Dlinet Jan 03 '13 at 16:56
1

@Dlinet, you are trying to learn some useful lessons on how to organize a program. I won't post code because there is already a really excellent answer; I cannot possibly improve upon it. But I would like to recommend a book to you.

The book is called Software Tools in Pascal. The language is Pascal, not C, but for reading the book this will cause no serious hardship. They start out implementing simple tools like the one in this example (which on UNIX is called cat) and they move on to more advanced stuff. Not only do they teach great lessons on how to organize this sort of program, they also cover language design issues. (There are problems in Pascal that really vex them, and if you know C you will realize that C doesn't have those problems.)

The book is out of print now, but I found it to be hugely valuable when I was learning to write code. The so-called "left corner design" methodology serves me well to this day.

I encourage you to find a used copy on Amazon or wherever. Amazon has used copies starting at $0.02 plus $4 shipping.

http://www.amazon.com/Software-Tools-Pascal-Brian-Kernighan/dp/0201103427

It would be an educational exercise to study the programs in this book and implement them in C. Any Linux system already has more-powerful and fully-debugged versions of these programs, but it would not be a waste of your time to work through this book and learn how to write this stuff.

Alternatively you could install FreePascal on your computer and use it to run the programs from the book.

Good luck and may you always enjoy software development!

steveha
  • 67,444
  • 18
  • 86
  • 112
  • +1: I learned a lot from the original [Software Tools](http://www.amazon.com/Software-Tools-Brian-W-Kernighan/dp/020103669X/) (written using Ratfor — Rational Fortran). That, along with [The Elements of Programming Style](http://www.amazon.com/Elements-Programming-Style-Brian-Kernighan/dp/0070342075) were very formative books for me, all those many years ago (30 of them, and a maybe a few to spare). But this is somewhat tangential to the direct question. – Jonathan Leffler Dec 28 '12 at 15:59
0

If you want something prebaked, there's cat on POSIX systems.

If you want to write it yourself, here's the basic layout:

  1. Check to make sure file name, permissions, and path are valid
  2. Read til newline separator in a loop (\n on Unix, \r\n on Windows/DOS)
  3. Check for error. If so, print error an abort.
  4. Print line to screen.
  5. Repeat

The point is, there isn't really a specific way to do it. Just read, then write, and repeat. With some error checking, you've got cat all over again.

Linuxios
  • 31,993
  • 12
  • 82
  • 110
  • Thank you, but what I am looking for is the best way to do this in the c programming language. The conceptual framework you have provided is sound, but isn't language specific. Example: I am a python programmer and in python this process is extremely simple: f=open(filepath,"r") for line in f: print line f.close() – Dlinet Dec 28 '12 at 00:11
  • @Dlinet: The point I'm making is that there is no particularly wonderful or pretty way to do this. Just in C, a lot of file code. A lot for a simple task. In Ruby, I can do this in one line, but C takes much more. Look at the other answer for the code. It's a lot. – Linuxios Dec 28 '12 at 00:12
  • I have looked at some other answers. For example: the third link I posted is 11 lines, but is only for fixed lines. – Dlinet Dec 28 '12 at 00:27