共用方式為


A one-line program to count lines of code

I wanted to sum the total lines of code in files in a given folder. I thought that writing my own program to do this would be faster than looking for it on the internet, so here's what I came up with (1 line broken into 7 lines to fit into your blog reader):

 using System;
using System.Linq;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(
            Directory.GetFiles(
                Environment.CurrentDirectory, "*", 
                string.Join(" ", args).Contains("/s") 
                    ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
            .Select(f => File.ReadAllLines(f).Length)
            .Sum());
    }
}

Just name the executable loc.exe and put it into your PATH - you're good to go. Input "loc" in the command prompt to get the total number of LOC in the current directory, and "loc /s" to do recursive search.

Please note that the way I wrote this program is not very good for debugging, because you can't put a breakpoint on substatements (technically speaking, this program consists of only one statement). In production code, I would rather write something like this:

 string path = Environment.CurrentDirectory;
string pattern = "*";
string commandLine = string.Join(" ", args);
SearchOption searchRecursively = commandLine.Contains("/s") 
            ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

string[] files = Directory.GetFiles(path, pattern, searchRecursively);
IEnumerable<int> lengths = files.Select(f => File.ReadAllLines(f).Length);
int totalLOC = lengths.Sum();

Console.WriteLine(totalLOC);

because it better conveys the step-by-step actions and allows to put a breakpoint on any step. However, for my little program, my sense of style guided me to use the former notation.

As another side note, the "production version" doesn't use var for type inference. I think it improves readability in this case.

Comments

  • Anonymous
    November 30, 2008
    One should use *.cs for C#-only files.

  • Anonymous
    November 30, 2008
    The comment has been removed

  • Anonymous
    November 30, 2008
    Just to clarify, I'm not proud or showing off or anything. This is just a posting so that this little trivial program is there when I need it - storing it in the cloud is better than somewhere deep in my harddrive. Luke Hoban wrote a ray-tracer as a single statement - now THAT's something worth showing off.

  • Anonymous
    December 02, 2008
    The comment has been removed

  • Anonymous
    December 02, 2008
    Jon, I agree :) Your LineReader does it the right way (stream). In my defense, I needed the program to count 20 kilobytes worth of files, so it took me about one minute to write this program without external dependencies. And, remembering that "Optimization is the root of all evil in programming", I didn't bother. :) It's a pity, actually, that IEnumerable<T> is not a native citizen in the framework from the very beginning. The Haskell's (and Comega's) list paradigm would make a lot of stuff better, e.g. your LineReader would be used instead of the current APIs etc. A lot of APIs use arrays where they could use IEnumerables (e.g. Reflection, or File IO, or string operations like Join or Split). In your book, you wrote about Streams not implementing IEnumerable yourself :) I might blog more about this someday.

  • Anonymous
    December 02, 2008
    How about: $ find . -name "*" | xargs wc -l Good grief.

  • Anonymous
    December 03, 2008
    That was awesome! > How about: > $ find . -name "*" | xargs wc -l > Good grief.

  • Anonymous
    December 03, 2008
    Hm, I usually use "sloccount" which is much more precise :)

  • Anonymous
    December 03, 2008
    It was pointed out to me on dzone that my one-liner doesn't take into account the recursive argument. So for non-recursive use find's -maxdepth argument. And yeah--I had totally forgotten about sloccount, which is more meaningful in many cases!

  • Anonymous
    December 03, 2008
    The comment has been removed