计算 NLOC 的简单脚本?

发布于 2024-07-10 03:48:41 字数 89 浏览 5 评论 0原文

您知道一个计算 NLOC(netto 代码行数)的简单脚本吗? 该脚本应该计算 C 代码的行数。 它不应该计算空行或仅带有大括号的行。 但它也不需要过于精确。

Do you know a simple script to count NLOCs (netto lines of code). The script should count lines of C Code. It should not count empty lines or lines with just braces. But it doesn't need to be overly exact either.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

煮茶煮酒煮时光 2024-07-17 03:48:41

我会使用 awk & 来做到这一点 cpp(预处理器)& 厕所。 awk 删除所有大括号和空格,预处理器删除所有注释,wc 计算行数:

find . -name \*.cpp -o -name \*.h | xargs -n1 cpp -fpreprocessed -P | 
    awk '!/^[{[:space:]}]*$/' | wc -l

如果您想包含注释:

find . -name \*.cpp -o -name \*.h | xargs awk '!/^[{[:space:]}]*$/' | wc -l

I would do that using awk & cpp (preprocessor) & wc . awk removes all braces and blanks, the preprocessor removes all comments and wc counts the lines:

find . -name \*.cpp -o -name \*.h | xargs -n1 cpp -fpreprocessed -P | 
    awk '!/^[{[:space:]}]*$/' | wc -l

If you want to have comments included:

find . -name \*.cpp -o -name \*.h | xargs awk '!/^[{[:space:]}]*$/' | wc -l
南…巷孤猫 2024-07-17 03:48:41

在网上查找 NLOC,我发现大部分是“未注释的代码行”。
您没有指定是否必须跳过注释...
因此,如果我坚持您当前的消息,Perl 中的以下一行应该可以完成这项工作:

perl -pe "s/^\s*[{}]?\s*\n//" Dialog.java | wc -l

我可以扩展它来处理行注释:

perl -pe "s#^\s*[{}]?\s*\n|^\s*//.*\n##" Dialog.java | wc -l

或者

perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" Dialog.java | wc -l

处理块注释可能稍微棘手一些(我不是 Perl 专家!)。

[编辑] 明白了...第一部分可能可以改进(更短)。 实验很有趣。

perl -e "$x = join('', <>); $x =~ s#/\*.*?\*/##gs; print $x" Dialog.java | perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" | wc -l

PS.:我使用双引号是因为我在 Windows 上测试过......

Looking NLOC on the Net, I found mostly "Non-commented lines of code".
You don't specify if comments must be skipped...
So if I stick to your current message, the following one-liner in Perl should do the job:

perl -pe "s/^\s*[{}]?\s*\n//" Dialog.java | wc -l

I can extend it to handle line comments:

perl -pe "s#^\s*[{}]?\s*\n|^\s*//.*\n##" Dialog.java | wc -l

or perhaps

perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" Dialog.java | wc -l

Handling block comments is slightly more tricky (I am not a Perl expert!).

[EDIT] Got it... First part can be probably improved (shorter). Was fun to experiment with.

perl -e "$x = join('', <>); $x =~ s#/\*.*?\*/##gs; print $x" Dialog.java | perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" | wc -l

PS.: I use double quotes because I tested on Windows...

半夏半凉 2024-07-17 03:48:41

查看 Visual Studio 的 DPack 插件。 它有任何解决方案/项目的统计报告。

Check out DPack plugin for Visual Studio. It has a stats report for any solution/project.

所谓喜欢 2024-07-17 03:48:41

不是脚本,但您可以尝试这个命令行开源工具:NLOC

Not a script, but you can try this command-line open source tool: NLOC

初心 2024-07-17 03:48:41

源监视器是免费软件源分析软件。 它是 Windows 应用程序,但也可以使用命令行参数运行。

它可以分析 C++、C、C#、VB.NET、Java、Delphi、Visual Basic (VB6) 或 HTML。

Source monitor is freeware source analysis software. It is windows application but it also can be run with parameters from command line.

It can analyze C++, C, C#, VB.NET, Java, Delphi, Visual Basic (VB6) or HTML.

瞎闹 2024-07-17 03:48:41

Ohloh 提供免费的Ohcount 用于计算代码行数和注释行数。

Ohloh offers the free Ohcount which counts lines of code and comments.

若相惜即相离 2024-07-17 03:48:41

如果注释还可以,标准的unix工具就足够了:

grep  -x -v "[[:space:]}{]*" files.c  | wc

If the comments can still be in, the standard unix tool are sufficent:

grep  -x -v "[[:space:]}{]*" files.c  | wc
梓梦 2024-07-17 03:48:41

SLOCCOunt 不是一个简单的脚本,它的功能远远超出您的需要。 然而,它是已经提到的 Ohcount 和 NLOC 的强大替代品。 :)

SLOCCOunt is not a simple script and does much more than what you need. However, it is a powerful alternative to the already mentioned Ohcount and NLOC. :)

若水般的淡然安静女子 2024-07-17 03:48:41

我通常只是这样做:

grep -vc '^

仅当您的空行确实是空的(没有空格)时才有效。 对我来说足够了。

(my files)

仅当您的空行确实是空的(没有空格)时才有效。 对我来说足够了。

I usually just do this:

grep -vc '^

Works only if your empty lines are really empty (no spaces). Sufficient for me.

(my files)

Works only if your empty lines are really empty (no spaces). Sufficient for me.

天荒地未老 2024-07-17 03:48:41

Locmetrics 效果很好。

Locmetrics works well.

|煩躁 2024-07-17 03:48:41

这是一个简单的 Perl 脚本 eLOC.pl

#!/usr/bin/perl -w
# eLOC - Effective Lines of Code Counter
# JFS (2005)
#
# $ perl eLOC.pl --help
#
use strict;
use warnings;
use sigtrap;
use diagnostics;

use warnings::register;
no warnings __PACKAGE__;
sub DEBUG { 0 }

use English qw( -no_match_vars ) ;  # Avoids regex performance penalty
use Getopt::Long qw(:config gnu_getopt);
use File::DosGlob 'glob';
use Pod::Usage;


our $VERSION = '0.01';

# globals
use constant NOTFILENAME => undef;
my %counter = ( 
    'PHYS'          => 0, 
    'ELOC'          => 0, 
    'PURE_COMMENT'  => 0,
    'BLANK'         => 0,
    'LLOC'          => 0,
    'INLINE_COMMENT'=> 0,
    'LOC'           => 0,
);
my %header = (
    "eloc"      =>  "eloc",
    "lloc"      =>  "lloc",
    "loc"       =>  "loc",
    "comment"   =>  "comment",
    "blank"     =>  "blank",
    "newline"   =>  "newline",
    "logicline" =>  "lgcline",
);
my %total = %counter; # copy
my $c = \%counter; # see format below
my $h = \%header;  # see top format below
my $inside_multiline_comment = 0;
my $filename = NOTFILENAME;
my $filecount = 0;
my $filename_header = "file name";

# process input args
my $version = '';
my $help = '';
my $man = '';
my $is_deterministic = '';
my $has_header = '';

print STDERR "Input args:'" if DEBUG;
print STDERR (join("|",@ARGV),"'\n") if DEBUG;

my %option = ('version' => \$version,
    'help' => \$help,   
    'man'  => \$man,
    'deterministic' => \$is_deterministic,
    'header' => \$has_header
);
GetOptions( \%option, 'version',    'help', 'man',
    'eloc|e', # print the eLOC counts
    'lloc|s', # print the lLOC counts (code statements)
    'loc|l' , # print the LOC counts (eLOC + lines of a single brace or parenthesis)
    'comment|c'    , # print the comments counts (count lines which contains a comment)
    'blank|b'      , # print the blank counts
    'newline|n'    , # print the newline count
    'logicline|g'  , # print the logical line count (= LOC + Comment Lines + Blank Lines)   
    'deterministic', # print the LOC determination for every line in the source file
    'header',        # print header line
) or invalid_options("$0: invalid options\nTry `$0 --help' for more information.");

version()                                   if $version;
pod2usage(-exitstatus => 0, -verbose => 1)  if $help ;
pod2usage(-exitstatus => 0, -verbose => 2)  if $man;

#
$has_header = 1 if $is_deterministic && $has_header eq '';

#format for print_loc_metric()
my ($format, $format_top) = make_format(); 
print STDERR "format:\n" if DEBUG > 10;
print STDERR $format if DEBUG > 10;
eval $format;
die $@ if $@; # $EVAL_ERROR

if(DEBUG>10) {
    print STDERR ("format_top:\n", $format_top);
}
if( $has_header) {
    eval $format_top;
    die $@ if $@; # $EVAL_ERROR 
}

# process files
print STDERR ("Input args after Getopts():\n",
    join("|",@ARGV),"\n") if DEBUG > 10;

expand_wildcards();
@ARGV = '-' unless @ARGV;
foreach my $fn (@ARGV) {
    $filename = $fn;
    unless (open(IN, "<$filename")) {
        warn "$0: Unable to read from '$filename': $!\n";
        next;
    }
    print STDERR "Scanning $filename...\n" if DEBUG;

    clear_counters();
    generate_loc_metric();  

    $filecount++;

    print_loc_metric();                     

    close(IN)
        or warn "$0: Could not close $filename: $!\n";      
}

# print total
if($filecount > 1) {
    $filename = "total";
    $c = \%total;
    print_loc_metric();
}
exit 0;

#-------------------------------------------------
sub wsglob {
    my @list = glob;
    @list ? @list : @_; #HACK: defence from emtpy list from glob()
}
sub expand_wildcards {
    print STDERR ("Input args before expand_wildcards():\n",
        join("|",@ARGV),"\n") if DEBUG;

    {       
        @ARGV = map( /['*?']/o ? wsglob($_) : $_ , @ARGV);
    }   
    print STDERR ("Input args after expand_wildcards():\n",
        join("|",@ARGV),"\n") if DEBUG; 
}
sub clear_counters {
    for my $name ( keys %counter) {
        $counter{$name} = 0;
    }   
}
sub make_format {
    my $f = 'format STDOUT =' . "\n";
    $f .= '# LOC, eLOC, lLOC, comment, blank, newline, logicline and filename' . "\n";
    my $f_top = 'format STDOUT_TOP =' . "\n";   
    my $console_screen_width = (get_terminal_size())[0];
    print STDERR '$console_screen_width=' . $console_screen_width ."\n" if DEBUG>10;
    $console_screen_width = 100 if $console_screen_width < 0;
    my $is_print_specifiers_set = 
         ($option{"eloc"} or
         $option{"lloc"} or
         $option{"loc"} or
         $option{"comment"} or
         $option{"blank"} or
         $option{"newline"} or
         $option{"logicline"});

    my %o = %option;
    my $fc = 0;
    if( $is_print_specifiers_set ) {

        $fc++ if $o{"eloc"};
        $fc++ if $o{"lloc"};
        $fc++ if $o{"loc"};
        $fc++ if $o{"comment"};
        $fc++ if $o{"blank"};
        $fc++ if $o{"newline"};
        $fc++ if $o{"logicline"};
        if( $fc == 0 ) { die "$0: assertion failed: field count is zero" }
    }
    else {
        # default
        $fc = 7;
        $o{"loc"}       = 1;       
        $o{"eloc"}      = 1;         
        $o{"lloc"}      = 1;      
        $o{"comment"}   = 1;   
        $o{"blank"}     = 1;     
        $o{"newline"}   = 1;   
        $o{"logicline"} = 1;        
    }
    if (DEBUG > 10) {
        while( (my ($name, $value) = each %{o}) ) {
            print STDERR "name=$name, value=$value\n";
        }       
    }


    # picture line  
    my $field_format = '@>>>>>> ';
    my $field_width = length $field_format;
    my $picture_line = $field_format x $fc;     
    #   place for filename
    $picture_line .= '^';       
    $picture_line .= '<' x ($console_screen_width - $field_width * $fc - 2);
    $picture_line .= "\n";  
    $f .= $picture_line;
    $f_top .= $picture_line;
    # argument line
    $f .=   '$c{"LOC"}, '      ,$f_top .= '$h{"loc"}, '       if $o{"loc"};
    $f .=   '$c{"ELOC"}, '     ,$f_top .= '$h{"eloc"}, '      if $o{"eloc"};      
    $f .=   '$c{"LLOC"}, '     ,$f_top .= '$h{"lloc"}, '      if $o{"lloc"};
    $f .=   '$c{"comment"}, '  ,$f_top .= '$h{"comment"}, '   if $o{"comment"};
    $f .=   '$c{"BLANK"}, '    ,$f_top .= '$h{"blank"}, '     if $o{"blank"};
    $f .=   '$c{"PHYS"}, '     ,$f_top .= '$h{"newline"}, '   if $o{"newline"};
    $f .=   '$c{"logicline"}, ',$f_top .= '$h{"logicline"}, ' if $o{"logicline"};
    $f .=  '$filename' . "\n";
    $f_top .= '$filename_header' . "\n";        

    # 2nd argument line for long file names
    $f .=   '^';    
    $f .= '<' x ($console_screen_width-2);
    $f .= '~~'  . "\n"
            .' $filename'  . "\n";
    $f .='.' . "\n";
    $f_top .='.' . "\n";
    return ($f, $f_top);
}
sub generate_loc_metric {
    my $is_concatinated = 0;
    LINE: while(<IN>)
    {
        chomp;      
        print if $is_deterministic && !$is_concatinated;        

        # handle multiline code statements
        if ($is_concatinated = s/\\$//) {
            warnings::warnif("$0: '\\'-ending line concantinated");
            increment('PHYS');
            print "\n" if $is_deterministic;
            my $line = <IN>;
            $_ .= $line;
            chomp($line);
            print $line if $is_deterministic;
            redo unless eof(IN);            
        }               

        # blank lines, including inside comments, don't move to next line here
        increment('BLANK')                  if( /^\s*$/ );  

        # check whether multiline comments finished
        if( $inside_multiline_comment && m~\*/\s*(\S*)\s*$~ ) {
            $inside_multiline_comment = 0;
            # check the rest of the line if it contains non-whitespace characters
            #debug $_ = $REDO_LINE . $1, redo LINE if($1);
            warnings::warnif("$0: expression '$1' after '*/' discarded") if($1);
            # else mark as pure comment
            increment('PURE_COMMENT');
            next LINE;
        }
        # inside multiline comments
        increment('PURE_COMMENT'), next LINE if( $inside_multiline_comment );

        # C++ style comment at the begining of line (except whitespaces)
        increment('PURE_COMMENT'), next LINE if( m~^\s*//~ ); 

        # C style comment at the begining of line (except whitespaces)
        if ( m~^\s*/\*~ ) {
            $inside_multiline_comment = 1 unless( m~\*/~ );
            increment('PURE_COMMENT'),  next LINE;
        }
        # inline comment, don't move to next line here
        increment('INLINE_COMMENT')        if ( is_inline_comment($_) );

        # lLOC implicitly incremented inside is_inline_comment($)

        #
        increment('LOC')                    unless( /^\s*$/ );

        # standalone braces or parenthesis  
                                  next LINE if( /^\s*(?:\{|\}|\(|\))+\s*$/ );           

        # eLOC is not comments, blanks or standalone braces or parenthesis
        #   therefore just increment eLOC counter here
        increment('ELOC'),        next LINE unless( /^\s*$/ );
    }
    continue {
        increment('PHYS');
        print " [$.]\n" if $is_deterministic; # $INPUT_LINE_NUMBER
    }
}

sub print_loc_metric {
    $c{'comment'} = $c{'PURE_COMMENT'} + $c{'INLINE_COMMENT'}; 
    # LOC + Comment Lines + Blank Lines 
    $c{'logicline'} = $c{'LOC'} + $c{'comment'} + $c{'BLANK'};
    unless (defined $filename) { 
        die "print_loc_metric(): filename is not defined";
    }       

    my $fn = $filename;
    $filename = "", $filename_header = "" 
        unless($#ARGV);
    print STDERR ("ARGV in print_loc_metric:" , join('|',@ARGV), "\n") 
        if DEBUG;
    write STDOUT; # replace with printf
    $filename = $fn;
}
sub increment {
    my $loc_type = shift;
    defined $loc_type
        or die 'increment(\$): input argument is undefined';    

    $counter{$loc_type}++;
    $total{$loc_type}++;
    print "\t#". $loc_type ."#" if $is_deterministic;   
}

sub is_inline_comment {
    my $line = shift;
    defined $line 
        or die 'is_inline_comment($): $line is not defined';

    print "\n$line" if DEBUG > 10;  

# here: line is not empty, not begining both C and C++ comments signs,
#       not standalone '{}()', not inside multiline comment,
#       ending '\' removed (joined line created if needed)

# Possible cases: 
#   - no C\C++ comment signs                        => is_inline_comment = 0
#   - C++ comment (no C comment sign)
#       * no quote characters                       => is_inline_comment = 1
#       * at least one comment sign is not quoted   => is_inline_comment = 1
#       * all comment signs are quoted              => is_inline_comment = 0
#   - C comment (no C++ comment sign)
#       * no quote characters                       => is_inline_comment = 1,
#           ~ odd number of '/*' and '*/'           => $inside_multiple_comment = 1                             
#           ~ even number                           => $inside_multiple_comment = 0
#       * etc...
#   - ...
# algorithm: move along the line from left to right
# rule: quoted comments are not counted
# rule: quoted by distinct style quotes are not counted
# rule: commented quotes are not counted
# rule: commented distinct style comments are not counted
# rule: increment('LLOC') if not-quoted, not-commented
#           semi-colon presents in the line except that two 
#           semi-colon in for() counted as one.

# 
$_ = $line; #hack: $_ = $line inside sub
#   state
my %s = (
    'c'     => 0, # c slash star - inside c style comments
    'cpp'   => 0, # c++ slash slash - inside C++ style comment
    'qm'    => 0, # quoted mark - inside quoted string
    'qqm'   => 0, # double quoted - inside double quoted string
);
my $has_comment = 0;
# find state
LOOP:
    {
      /\G\"/gc  &&  do { # match double quote
                            unless( $s{'qm'} || $s{'c'} || $s{'cpp'} ) {
                                    # toggle 
                                $s{'qqm'} = $s{'qqm'} ? 0 : 1; 
                            }
                            redo LOOP;
                    };
      /\G\'/gc  &&  do { # match single quote
                            unless( $s{'qqm'} || $s{'c'} || $s{'cpp'} ) {
                                    # toggle 
                                $s{'qm'} = $s{'qm'} ? 0 : 1; 
                            }
                            redo LOOP;
                    };
      m~\G//~gc &&  do { # match C++ comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'c'} ) {
                                    # on
                                $has_comment = 1;
                                $s{'cpp'} = 1; 
                            } 
                            redo LOOP;
                    };
      m~\G/\*~gc    &&  do { # match begining C comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} ) {
                                    # on
                                $has_comment = 1;
                                $s{'c'} = $s{'c'} ? 1 : 1; 
                            } 
                            redo LOOP;
                    };
      m~\G\*/~gc    &&  do { # match ending C comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} ) {
                                    # off                                   
                                if( $s{'c'} ) {                                     
                                    $s{'c'} = 0;
                                }
                                else {
                                    die 'is_inline_comment($): unexpected c style ending comment sign'.
                                        "\n'$line'";
                                }
                            } 
                            redo LOOP;
                    };
      /\Gfor\s*\(.*\;.*\;.*\)/gc    &&  do { # match for loop
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'} ) {
                                # not-commented, not-quoted semi-colon                                  
                                increment('LLOC');
                            }
                            redo LOOP;
                    };                                          
      /\G\;/gc  &&  do { # match semi-colon
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'} ) {
                                # not-commented, not-quoted semi-colon
                                # not inside for() loop
                                increment('LLOC');
                            }
                            redo LOOP;
                    };                      
      /\G./gc   &&  do { # match any other character
                            # skip 1 character
                            redo LOOP;
                    };
      /\G$/gc   &&  do { # match end of the line
                            last LOOP;
                    };                      
      #default
        die 'is_inline_comment($): unexpected character in the line:' .
            "\n'$line'";
    }
# apply state
    $inside_multiline_comment = $s{'c'};
    return $has_comment;
}

sub version {
#   TODO: version implementation
    print <<"VERSION";
NAME v$VERSION
Written by AUTHOR

COPYRIGHT AND LICENSE
VERSION

exit 0;
}

sub invalid_options {
    print STDERR (@_ ,"\n");
    exit 2;
}

sub get_terminal_size {
    my ($wchar, $hchar) = ( -1, -1); 
    my $win32console = <<'WIN32_CONSOLE';   
        use Win32::Console; 
        my $CONSOLE = new Win32::Console(); 
        ($wchar, $hchar) = $CONSOLE->MaxWindow();
WIN32_CONSOLE

    eval($win32console); 
    return ($wchar, $hchar) unless( $@ );
    warnings::warnif($@); # $EVAL_ERROR

    my $term_readkey = <<'TERM_READKEY';
        use Term::ReadKey; 
        ($wchar,$hchar, $wpixels, $hpixels) = GetTerminalSize(); 
TERM_READKEY

    eval($term_readkey); 
    return ($wchar, $hchar) unless( $@ );

    warnings::warnif($@); # $EVAL_ERROR 
    my $ioctl = <<'IOCTL'; 
        require 'sys/ioctl.ph'; 
        die "no TIOCGWINSZ " unless defined &TIOCGWINSZ; 
        open(TTY, "+</dev/tty")                     
            or die "No tty: $!"; 
        unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) { 
            die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", 
                    &TIOCGWINSZ; 
        } 
        ($hchar, $wchar, $xpixel, $ypixel) = 
            unpack('S4', $winsize); # probably $hchar & $wchar should be swapped here 
IOCTL

    eval($ioctl); 
    warnings::warnif($@) if $@ ; # $EVAL_ERROR  

    return ($wchar, $hchar); 
}

1;
__END__ 

=head1 NAME

eLOC - Effective Lines of Code Counter

=head1 SYNOPSIS

B<eloc> B<[>OPTIONB<]...> B<[>FILEB<]...>

Print LOC, eLOC, lLOC, comment, blank, newline and logicline counts 
for each FILE, and a total line if more than one FILE is specified.
See L</"LOC Specification"> for more info, use `eloc --man'.

  -e, --eloc             print the {E}LOC counts
  -s, --lloc             print the lLOC counts (code {S}tatements)
  -l, --loc              print the {L}OC counts (eLOC + lines of a single brace or parenthesis)
  -c, --comment          print the {C}omments counts (count lines which contains a comment)
  -b, --blank            print the {B}lank counts
  -n, --newline          print the {N}ewline count
  -g, --logicline        print the lo{G}ical line count (= LOC + Comment Lines + Blank Lines)
      --deterministic    print the LOC determination for every line in the source file
      --header           print header line
      --help display this help and exit
      --man  display full help and exit
      --version  output version information and exit

With no FILE, or when FILE is -, read standard input.      

Metrics counted by the program are based on narration from 
http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=for TODO: Comment Percent = Comment Line Count / Logical Line Count ) x 100      

=for TODO: White Space Percentage = (Number of spaces / Number of spaces and characters) * 100       

=head1 DESCRIPTION

eLOC is a simple LOC counter. See L</"LOC Specification">. 

=head2 LOC Specification

=over 1

=item LOC

Lines Of Code = eLOC + lines of a single brace or parenthesis

=item eLOC

An effective line of code or eLOC is the measurement of all lines that are 
not comments, blanks or standalone braces or parenthesis. 
This metric more closely represents the quantity of work performed. 
RSM introduces eLOC as a metrics standard.
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=item lLOC

Logical lines of code represent a metrics for those line of code which form 
code statements.  These statements are terminated with a semi-colon.  

The control line for the "for" loop contain two semi-colons but accounts 
for only one semi colon.
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=item comment

comment = pure comment + inline comment



=over

=item pure comment

Comment lines represent a metrics for pure comment line without any code in it.
See L</"inline comment">.

=item inline comment

Inline comment line is a line which contains both LOC line and pure comment.

Inline comment line and pure comment line (see L</"pure comment">)
are mutually exclusive, that is a given physical line cannot be an inline comment
line and a pure comment line simultaneously.

=over

=item Example:

    static const int defaultWidth = 400;        // value provided in declaration

=back

=back

=item blank

Blank line is a line which contains at most whitespaces.
Blank lines are counted inside comments too.

=item logicline

The logical line count = LOC + Comment Lines + Blank Lines

=back

=head1 KNOWN BUGS AND LIMITATIONS

=over

=item

It supports only C/C++ source files.

=item

Comments inside for(;;) statements are not counted

=over

=item Example:

    for(int i = 0; i < N /*comment*/; i++ );        #LLOC#  #LLOC#  #LOC#   #ELOC#  #PHYS# [1]

=back

=item

'\'-ending lines are concatinated ( though newline count is valid)

=item

Input from stdin is not supported in the case 
the script is envoked solely by name without explicit perl executable.

=item

Wildcards in path with spaces are not supported (like GNU utilities).

=back

=over

=begin fixed
=item Limitation: single source file

    Only one source file at time supported

=item Limitation: LLOC is unsupported

    The logical lines of code metric is unsupported. 

=item missed inline comment for C style comment

    #include <math.h> /* comment */ #ELOC#  #PHYS# [2]

But must be
    #include <math.h> /* comment */ #INLINE_COMMENT#    #ELOC#  #PHYS# [2]

=item wrong LOC type for the code after '*/'

    /* another  #PURE_COMMENT#  #PHYS# [36]
    trick   #PURE_COMMENT#  #PHYS# [37]
    */  i++;    #PURE_COMMENT#  #PHYS# [38]

In the last line must be 

    #INLINE_COMMENT# #PHYS# [38]

=end fixed

=back

=head1 SEE ALSO

Metrics counted by the program are based on narration from L<http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm>

=cut

Here's a simple Perl script eLOC.pl:

#!/usr/bin/perl -w
# eLOC - Effective Lines of Code Counter
# JFS (2005)
#
# $ perl eLOC.pl --help
#
use strict;
use warnings;
use sigtrap;
use diagnostics;

use warnings::register;
no warnings __PACKAGE__;
sub DEBUG { 0 }

use English qw( -no_match_vars ) ;  # Avoids regex performance penalty
use Getopt::Long qw(:config gnu_getopt);
use File::DosGlob 'glob';
use Pod::Usage;


our $VERSION = '0.01';

# globals
use constant NOTFILENAME => undef;
my %counter = ( 
    'PHYS'          => 0, 
    'ELOC'          => 0, 
    'PURE_COMMENT'  => 0,
    'BLANK'         => 0,
    'LLOC'          => 0,
    'INLINE_COMMENT'=> 0,
    'LOC'           => 0,
);
my %header = (
    "eloc"      =>  "eloc",
    "lloc"      =>  "lloc",
    "loc"       =>  "loc",
    "comment"   =>  "comment",
    "blank"     =>  "blank",
    "newline"   =>  "newline",
    "logicline" =>  "lgcline",
);
my %total = %counter; # copy
my $c = \%counter; # see format below
my $h = \%header;  # see top format below
my $inside_multiline_comment = 0;
my $filename = NOTFILENAME;
my $filecount = 0;
my $filename_header = "file name";

# process input args
my $version = '';
my $help = '';
my $man = '';
my $is_deterministic = '';
my $has_header = '';

print STDERR "Input args:'" if DEBUG;
print STDERR (join("|",@ARGV),"'\n") if DEBUG;

my %option = ('version' => \$version,
    'help' => \$help,   
    'man'  => \$man,
    'deterministic' => \$is_deterministic,
    'header' => \$has_header
);
GetOptions( \%option, 'version',    'help', 'man',
    'eloc|e', # print the eLOC counts
    'lloc|s', # print the lLOC counts (code statements)
    'loc|l' , # print the LOC counts (eLOC + lines of a single brace or parenthesis)
    'comment|c'    , # print the comments counts (count lines which contains a comment)
    'blank|b'      , # print the blank counts
    'newline|n'    , # print the newline count
    'logicline|g'  , # print the logical line count (= LOC + Comment Lines + Blank Lines)   
    'deterministic', # print the LOC determination for every line in the source file
    'header',        # print header line
) or invalid_options("$0: invalid options\nTry `$0 --help' for more information.");

version()                                   if $version;
pod2usage(-exitstatus => 0, -verbose => 1)  if $help ;
pod2usage(-exitstatus => 0, -verbose => 2)  if $man;

#
$has_header = 1 if $is_deterministic && $has_header eq '';

#format for print_loc_metric()
my ($format, $format_top) = make_format(); 
print STDERR "format:\n" if DEBUG > 10;
print STDERR $format if DEBUG > 10;
eval $format;
die $@ if $@; # $EVAL_ERROR

if(DEBUG>10) {
    print STDERR ("format_top:\n", $format_top);
}
if( $has_header) {
    eval $format_top;
    die $@ if $@; # $EVAL_ERROR 
}

# process files
print STDERR ("Input args after Getopts():\n",
    join("|",@ARGV),"\n") if DEBUG > 10;

expand_wildcards();
@ARGV = '-' unless @ARGV;
foreach my $fn (@ARGV) {
    $filename = $fn;
    unless (open(IN, "<$filename")) {
        warn "$0: Unable to read from '$filename': $!\n";
        next;
    }
    print STDERR "Scanning $filename...\n" if DEBUG;

    clear_counters();
    generate_loc_metric();  

    $filecount++;

    print_loc_metric();                     

    close(IN)
        or warn "$0: Could not close $filename: $!\n";      
}

# print total
if($filecount > 1) {
    $filename = "total";
    $c = \%total;
    print_loc_metric();
}
exit 0;

#-------------------------------------------------
sub wsglob {
    my @list = glob;
    @list ? @list : @_; #HACK: defence from emtpy list from glob()
}
sub expand_wildcards {
    print STDERR ("Input args before expand_wildcards():\n",
        join("|",@ARGV),"\n") if DEBUG;

    {       
        @ARGV = map( /['*?']/o ? wsglob($_) : $_ , @ARGV);
    }   
    print STDERR ("Input args after expand_wildcards():\n",
        join("|",@ARGV),"\n") if DEBUG; 
}
sub clear_counters {
    for my $name ( keys %counter) {
        $counter{$name} = 0;
    }   
}
sub make_format {
    my $f = 'format STDOUT =' . "\n";
    $f .= '# LOC, eLOC, lLOC, comment, blank, newline, logicline and filename' . "\n";
    my $f_top = 'format STDOUT_TOP =' . "\n";   
    my $console_screen_width = (get_terminal_size())[0];
    print STDERR '$console_screen_width=' . $console_screen_width ."\n" if DEBUG>10;
    $console_screen_width = 100 if $console_screen_width < 0;
    my $is_print_specifiers_set = 
         ($option{"eloc"} or
         $option{"lloc"} or
         $option{"loc"} or
         $option{"comment"} or
         $option{"blank"} or
         $option{"newline"} or
         $option{"logicline"});

    my %o = %option;
    my $fc = 0;
    if( $is_print_specifiers_set ) {

        $fc++ if $o{"eloc"};
        $fc++ if $o{"lloc"};
        $fc++ if $o{"loc"};
        $fc++ if $o{"comment"};
        $fc++ if $o{"blank"};
        $fc++ if $o{"newline"};
        $fc++ if $o{"logicline"};
        if( $fc == 0 ) { die "$0: assertion failed: field count is zero" }
    }
    else {
        # default
        $fc = 7;
        $o{"loc"}       = 1;       
        $o{"eloc"}      = 1;         
        $o{"lloc"}      = 1;      
        $o{"comment"}   = 1;   
        $o{"blank"}     = 1;     
        $o{"newline"}   = 1;   
        $o{"logicline"} = 1;        
    }
    if (DEBUG > 10) {
        while( (my ($name, $value) = each %{o}) ) {
            print STDERR "name=$name, value=$value\n";
        }       
    }


    # picture line  
    my $field_format = '@>>>>>> ';
    my $field_width = length $field_format;
    my $picture_line = $field_format x $fc;     
    #   place for filename
    $picture_line .= '^';       
    $picture_line .= '<' x ($console_screen_width - $field_width * $fc - 2);
    $picture_line .= "\n";  
    $f .= $picture_line;
    $f_top .= $picture_line;
    # argument line
    $f .=   '$c{"LOC"}, '      ,$f_top .= '$h{"loc"}, '       if $o{"loc"};
    $f .=   '$c{"ELOC"}, '     ,$f_top .= '$h{"eloc"}, '      if $o{"eloc"};      
    $f .=   '$c{"LLOC"}, '     ,$f_top .= '$h{"lloc"}, '      if $o{"lloc"};
    $f .=   '$c{"comment"}, '  ,$f_top .= '$h{"comment"}, '   if $o{"comment"};
    $f .=   '$c{"BLANK"}, '    ,$f_top .= '$h{"blank"}, '     if $o{"blank"};
    $f .=   '$c{"PHYS"}, '     ,$f_top .= '$h{"newline"}, '   if $o{"newline"};
    $f .=   '$c{"logicline"}, ',$f_top .= '$h{"logicline"}, ' if $o{"logicline"};
    $f .=  '$filename' . "\n";
    $f_top .= '$filename_header' . "\n";        

    # 2nd argument line for long file names
    $f .=   '^';    
    $f .= '<' x ($console_screen_width-2);
    $f .= '~~'  . "\n"
            .' $filename'  . "\n";
    $f .='.' . "\n";
    $f_top .='.' . "\n";
    return ($f, $f_top);
}
sub generate_loc_metric {
    my $is_concatinated = 0;
    LINE: while(<IN>)
    {
        chomp;      
        print if $is_deterministic && !$is_concatinated;        

        # handle multiline code statements
        if ($is_concatinated = s/\\$//) {
            warnings::warnif("$0: '\\'-ending line concantinated");
            increment('PHYS');
            print "\n" if $is_deterministic;
            my $line = <IN>;
            $_ .= $line;
            chomp($line);
            print $line if $is_deterministic;
            redo unless eof(IN);            
        }               

        # blank lines, including inside comments, don't move to next line here
        increment('BLANK')                  if( /^\s*$/ );  

        # check whether multiline comments finished
        if( $inside_multiline_comment && m~\*/\s*(\S*)\s*$~ ) {
            $inside_multiline_comment = 0;
            # check the rest of the line if it contains non-whitespace characters
            #debug $_ = $REDO_LINE . $1, redo LINE if($1);
            warnings::warnif("$0: expression '$1' after '*/' discarded") if($1);
            # else mark as pure comment
            increment('PURE_COMMENT');
            next LINE;
        }
        # inside multiline comments
        increment('PURE_COMMENT'), next LINE if( $inside_multiline_comment );

        # C++ style comment at the begining of line (except whitespaces)
        increment('PURE_COMMENT'), next LINE if( m~^\s*//~ ); 

        # C style comment at the begining of line (except whitespaces)
        if ( m~^\s*/\*~ ) {
            $inside_multiline_comment = 1 unless( m~\*/~ );
            increment('PURE_COMMENT'),  next LINE;
        }
        # inline comment, don't move to next line here
        increment('INLINE_COMMENT')        if ( is_inline_comment($_) );

        # lLOC implicitly incremented inside is_inline_comment($)

        #
        increment('LOC')                    unless( /^\s*$/ );

        # standalone braces or parenthesis  
                                  next LINE if( /^\s*(?:\{|\}|\(|\))+\s*$/ );           

        # eLOC is not comments, blanks or standalone braces or parenthesis
        #   therefore just increment eLOC counter here
        increment('ELOC'),        next LINE unless( /^\s*$/ );
    }
    continue {
        increment('PHYS');
        print " [$.]\n" if $is_deterministic; # $INPUT_LINE_NUMBER
    }
}

sub print_loc_metric {
    $c{'comment'} = $c{'PURE_COMMENT'} + $c{'INLINE_COMMENT'}; 
    # LOC + Comment Lines + Blank Lines 
    $c{'logicline'} = $c{'LOC'} + $c{'comment'} + $c{'BLANK'};
    unless (defined $filename) { 
        die "print_loc_metric(): filename is not defined";
    }       

    my $fn = $filename;
    $filename = "", $filename_header = "" 
        unless($#ARGV);
    print STDERR ("ARGV in print_loc_metric:" , join('|',@ARGV), "\n") 
        if DEBUG;
    write STDOUT; # replace with printf
    $filename = $fn;
}
sub increment {
    my $loc_type = shift;
    defined $loc_type
        or die 'increment(\$): input argument is undefined';    

    $counter{$loc_type}++;
    $total{$loc_type}++;
    print "\t#". $loc_type ."#" if $is_deterministic;   
}

sub is_inline_comment {
    my $line = shift;
    defined $line 
        or die 'is_inline_comment($): $line is not defined';

    print "\n$line" if DEBUG > 10;  

# here: line is not empty, not begining both C and C++ comments signs,
#       not standalone '{}()', not inside multiline comment,
#       ending '\' removed (joined line created if needed)

# Possible cases: 
#   - no C\C++ comment signs                        => is_inline_comment = 0
#   - C++ comment (no C comment sign)
#       * no quote characters                       => is_inline_comment = 1
#       * at least one comment sign is not quoted   => is_inline_comment = 1
#       * all comment signs are quoted              => is_inline_comment = 0
#   - C comment (no C++ comment sign)
#       * no quote characters                       => is_inline_comment = 1,
#           ~ odd number of '/*' and '*/'           => $inside_multiple_comment = 1                             
#           ~ even number                           => $inside_multiple_comment = 0
#       * etc...
#   - ...
# algorithm: move along the line from left to right
# rule: quoted comments are not counted
# rule: quoted by distinct style quotes are not counted
# rule: commented quotes are not counted
# rule: commented distinct style comments are not counted
# rule: increment('LLOC') if not-quoted, not-commented
#           semi-colon presents in the line except that two 
#           semi-colon in for() counted as one.

# 
$_ = $line; #hack: $_ = $line inside sub
#   state
my %s = (
    'c'     => 0, # c slash star - inside c style comments
    'cpp'   => 0, # c++ slash slash - inside C++ style comment
    'qm'    => 0, # quoted mark - inside quoted string
    'qqm'   => 0, # double quoted - inside double quoted string
);
my $has_comment = 0;
# find state
LOOP:
    {
      /\G\"/gc  &&  do { # match double quote
                            unless( $s{'qm'} || $s{'c'} || $s{'cpp'} ) {
                                    # toggle 
                                $s{'qqm'} = $s{'qqm'} ? 0 : 1; 
                            }
                            redo LOOP;
                    };
      /\G\'/gc  &&  do { # match single quote
                            unless( $s{'qqm'} || $s{'c'} || $s{'cpp'} ) {
                                    # toggle 
                                $s{'qm'} = $s{'qm'} ? 0 : 1; 
                            }
                            redo LOOP;
                    };
      m~\G//~gc &&  do { # match C++ comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'c'} ) {
                                    # on
                                $has_comment = 1;
                                $s{'cpp'} = 1; 
                            } 
                            redo LOOP;
                    };
      m~\G/\*~gc    &&  do { # match begining C comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} ) {
                                    # on
                                $has_comment = 1;
                                $s{'c'} = $s{'c'} ? 1 : 1; 
                            } 
                            redo LOOP;
                    };
      m~\G\*/~gc    &&  do { # match ending C comment sign
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} ) {
                                    # off                                   
                                if( $s{'c'} ) {                                     
                                    $s{'c'} = 0;
                                }
                                else {
                                    die 'is_inline_comment($): unexpected c style ending comment sign'.
                                        "\n'$line'";
                                }
                            } 
                            redo LOOP;
                    };
      /\Gfor\s*\(.*\;.*\;.*\)/gc    &&  do { # match for loop
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'} ) {
                                # not-commented, not-quoted semi-colon                                  
                                increment('LLOC');
                            }
                            redo LOOP;
                    };                                          
      /\G\;/gc  &&  do { # match semi-colon
                            unless( $s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'} ) {
                                # not-commented, not-quoted semi-colon
                                # not inside for() loop
                                increment('LLOC');
                            }
                            redo LOOP;
                    };                      
      /\G./gc   &&  do { # match any other character
                            # skip 1 character
                            redo LOOP;
                    };
      /\G$/gc   &&  do { # match end of the line
                            last LOOP;
                    };                      
      #default
        die 'is_inline_comment($): unexpected character in the line:' .
            "\n'$line'";
    }
# apply state
    $inside_multiline_comment = $s{'c'};
    return $has_comment;
}

sub version {
#   TODO: version implementation
    print <<"VERSION";
NAME v$VERSION
Written by AUTHOR

COPYRIGHT AND LICENSE
VERSION

exit 0;
}

sub invalid_options {
    print STDERR (@_ ,"\n");
    exit 2;
}

sub get_terminal_size {
    my ($wchar, $hchar) = ( -1, -1); 
    my $win32console = <<'WIN32_CONSOLE';   
        use Win32::Console; 
        my $CONSOLE = new Win32::Console(); 
        ($wchar, $hchar) = $CONSOLE->MaxWindow();
WIN32_CONSOLE

    eval($win32console); 
    return ($wchar, $hchar) unless( $@ );
    warnings::warnif($@); # $EVAL_ERROR

    my $term_readkey = <<'TERM_READKEY';
        use Term::ReadKey; 
        ($wchar,$hchar, $wpixels, $hpixels) = GetTerminalSize(); 
TERM_READKEY

    eval($term_readkey); 
    return ($wchar, $hchar) unless( $@ );

    warnings::warnif($@); # $EVAL_ERROR 
    my $ioctl = <<'IOCTL'; 
        require 'sys/ioctl.ph'; 
        die "no TIOCGWINSZ " unless defined &TIOCGWINSZ; 
        open(TTY, "+</dev/tty")                     
            or die "No tty: $!"; 
        unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) { 
            die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", 
                    &TIOCGWINSZ; 
        } 
        ($hchar, $wchar, $xpixel, $ypixel) = 
            unpack('S4', $winsize); # probably $hchar & $wchar should be swapped here 
IOCTL

    eval($ioctl); 
    warnings::warnif($@) if $@ ; # $EVAL_ERROR  

    return ($wchar, $hchar); 
}

1;
__END__ 

=head1 NAME

eLOC - Effective Lines of Code Counter

=head1 SYNOPSIS

B<eloc> B<[>OPTIONB<]...> B<[>FILEB<]...>

Print LOC, eLOC, lLOC, comment, blank, newline and logicline counts 
for each FILE, and a total line if more than one FILE is specified.
See L</"LOC Specification"> for more info, use `eloc --man'.

  -e, --eloc             print the {E}LOC counts
  -s, --lloc             print the lLOC counts (code {S}tatements)
  -l, --loc              print the {L}OC counts (eLOC + lines of a single brace or parenthesis)
  -c, --comment          print the {C}omments counts (count lines which contains a comment)
  -b, --blank            print the {B}lank counts
  -n, --newline          print the {N}ewline count
  -g, --logicline        print the lo{G}ical line count (= LOC + Comment Lines + Blank Lines)
      --deterministic    print the LOC determination for every line in the source file
      --header           print header line
      --help display this help and exit
      --man  display full help and exit
      --version  output version information and exit

With no FILE, or when FILE is -, read standard input.      

Metrics counted by the program are based on narration from 
http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=for TODO: Comment Percent = Comment Line Count / Logical Line Count ) x 100      

=for TODO: White Space Percentage = (Number of spaces / Number of spaces and characters) * 100       

=head1 DESCRIPTION

eLOC is a simple LOC counter. See L</"LOC Specification">. 

=head2 LOC Specification

=over 1

=item LOC

Lines Of Code = eLOC + lines of a single brace or parenthesis

=item eLOC

An effective line of code or eLOC is the measurement of all lines that are 
not comments, blanks or standalone braces or parenthesis. 
This metric more closely represents the quantity of work performed. 
RSM introduces eLOC as a metrics standard.
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=item lLOC

Logical lines of code represent a metrics for those line of code which form 
code statements.  These statements are terminated with a semi-colon.  

The control line for the "for" loop contain two semi-colons but accounts 
for only one semi colon.
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm

=item comment

comment = pure comment + inline comment



=over

=item pure comment

Comment lines represent a metrics for pure comment line without any code in it.
See L</"inline comment">.

=item inline comment

Inline comment line is a line which contains both LOC line and pure comment.

Inline comment line and pure comment line (see L</"pure comment">)
are mutually exclusive, that is a given physical line cannot be an inline comment
line and a pure comment line simultaneously.

=over

=item Example:

    static const int defaultWidth = 400;        // value provided in declaration

=back

=back

=item blank

Blank line is a line which contains at most whitespaces.
Blank lines are counted inside comments too.

=item logicline

The logical line count = LOC + Comment Lines + Blank Lines

=back

=head1 KNOWN BUGS AND LIMITATIONS

=over

=item

It supports only C/C++ source files.

=item

Comments inside for(;;) statements are not counted

=over

=item Example:

    for(int i = 0; i < N /*comment*/; i++ );        #LLOC#  #LLOC#  #LOC#   #ELOC#  #PHYS# [1]

=back

=item

'\'-ending lines are concatinated ( though newline count is valid)

=item

Input from stdin is not supported in the case 
the script is envoked solely by name without explicit perl executable.

=item

Wildcards in path with spaces are not supported (like GNU utilities).

=back

=over

=begin fixed
=item Limitation: single source file

    Only one source file at time supported

=item Limitation: LLOC is unsupported

    The logical lines of code metric is unsupported. 

=item missed inline comment for C style comment

    #include <math.h> /* comment */ #ELOC#  #PHYS# [2]

But must be
    #include <math.h> /* comment */ #INLINE_COMMENT#    #ELOC#  #PHYS# [2]

=item wrong LOC type for the code after '*/'

    /* another  #PURE_COMMENT#  #PHYS# [36]
    trick   #PURE_COMMENT#  #PHYS# [37]
    */  i++;    #PURE_COMMENT#  #PHYS# [38]

In the last line must be 

    #INLINE_COMMENT# #PHYS# [38]

=end fixed

=back

=head1 SEE ALSO

Metrics counted by the program are based on narration from L<http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm>

=cut
骷髅 2024-07-17 03:48:41

以下脚本将获取给定目录中与模式匹配的所有文件的计数。

# 脚本开始

var str 文件
var str dir

set $files = "*.cpp" # <====================== 在此处设置文件名模式。
set $dir = "C:/myproject" # <====================== 在此处设置您的项目目录。

# 获取变量fileList中的文件列表。
var str 文件列表
查找 -rn 文件($files) dir($dir) > $fileList

# 声明变量,我们将在其中保存单个文件的计数。
var int c # 所有行
var int nb # 非空行

# 声明变量,我们将在其中保存所有文件的总计数。
var int Totalc # 所有行的总和
var int Totalnb # 所有非空白行的总和

# 声明我们将存储文件计数的变量。
var int fileCount

# 我们将在下面存储当前正在处理的文件的名称。
var str file

# 逐个浏览 $fileList 文件。
while ($fileList<>"")

# 提取下一个文件。
lex "1" $fileList >$file

# 检查这是否是平面文件。 我们对目录不感兴趣。
af $file >null # 我们不想看到输出。
# 我们只想设置 $ftype 变量。
if ($ftype=="f")

# 是的,这是一个平面文件。

 # Increment file count.<br>
set $fileCount = $fileCount+1<br>

# Collect the content of $file in $content<br>
var str content # Content of one file at a time<br>
repro $file >$content<br>

# Get count and non-blank count.<br>
set $c={len -e $content}<br>
set $nb={len $content}<br>

echo -e "File: " $file ", Total Count: " $c ", Non-blank Count: " $nb<br>

# Update total counts.<br>
set $totalc = $totalc + $c<br>
set $totalnb = $totalnb + $nb<br>

完成
endif

完成

显示总和

echo "************************************ ********************************************************** **************************************************”
echo "所有行总数:\t" $totalc ",\t非空白行总数:\t" $totalnb ", 文件总数:" $fileCount
回声“**************************************************** ********************************************************** ********************************"

# END OF SCRIPT

如果您想要年份修改的文件中的行数仅 2008 年,添加 ($fmtime >= "2008") 等。

如果您没有 biterscripting,请从 .com 获取。

The following script will get count of all file matching a pattern in a given directory.

# START OF SCRIPT

var str files
var str dir

set $files = "*.cpp" # <===================== Set your file name pattern here.
set $dir = "C:/myproject" # <===================== Set your project directory here.

# Get the list of files in variable fileList.
var str fileList
find -rn files($files) dir($dir) > $fileList

# Declare variables where we will save counts of individual files.
var int c # all lines
var int nb # non-blank lines

# Declare variables where we will save total counts for all files.
var int totalc # sum-total of all lines
var int totalnb # sum-total of all non-blank lines

# Declare variable where we will store file count.
var int fileCount

# We will store the name of the file we are working on currently, in the following.
var str file

# Go thru the $fileList one by one file.
while ($fileList<>"")
do
# Extract the next file.
lex "1" $fileList >$file

# Check if this is a flat file. We are not interested in directories.
af $file >null # We don't want to see the output.
# We only want to set the $ftype variable.
if ($ftype=="f")
do
# Yes, this is a flat file.

 # Increment file count.<br>
set $fileCount = $fileCount+1<br>

# Collect the content of $file in $content<br>
var str content # Content of one file at a time<br>
repro $file >$content<br>

# Get count and non-blank count.<br>
set $c={len -e $content}<br>
set $nb={len $content}<br>

echo -e "File: " $file ", Total Count: " $c ", Non-blank Count: " $nb<br>

# Update total counts.<br>
set $totalc = $totalc + $c<br>
set $totalnb = $totalnb + $nb<br>

done
endif

done

Show sum-totals

echo "**********************************************************************************************************************************"
echo "Total Count of all lines:\t" $totalc ",\tTotal Count of non-blank lines:\t" $totalnb ", Total files: " $fileCount
echo "**********************************************************************************************************************************"

# END OF SCRIPT

If you want line counts in files modified in year 2008 only, add ($fmtime >= "2008"), etc.

If you don't have biterscripting, get it from .com .

水染的天色ゝ 2024-07-17 03:48:41

这不是一个简单的脚本,但是 CCCC(C 和 C++ 代码计数器)已经存在了一段时间并且可以工作对我来说很棒。

Not a simple script, but CCCC (C and C++ Code Counter) has been around for a while and it works great for me.

平安喜乐 2024-07-17 03:48:41

我有一个名为 scc 的程序,它会删除 C 注释(和 C++ 注释,尽管在 C99 中它们是相同的)。 应用该过滤器来删除空白行,如果需要,还可以删除仅包含左大括号和右大括号的行,以生成行数。 我已经在内部项目中使用过它 - 不需要打折开/闭大括号。 这些脚本更加复杂,比较了存储在 ClearCase 中的一个重要项目的两个不同版本的源代码。 他们还对添加和删除的文件以及从公共文件中添加和删除的行等进行了统计。

不计算大括号会产生很大的差异:

Black JL: co -q -p scc.c | scc | sed '/^[       ]*$/d' | wc -l
     208
Black JL: co -q -p scc.c | scc | sed '/^[       {}]*$/d' | wc -l
     144
Black JL: co -p -q scc.c | wc -l
     271
Black JL:

因此,您的规则下有 144 行; 208 计算左支撑线和右支撑线; 271 计算一切。

让我知道您是否需要 scc 代码(发送电子邮件至 gmail dot com 的第一个点最后一个)。 它是 13 KB 的 gzip 压缩 tar 文件,包括手册页、酷刑测试和一些库文件。


@litb 评论说“cpp -fpreprocessed -P file”处理剥离
评论。 大部分都是这样。 但是,当我在压力测试中运行它时
对于 SCC,它在(在我看来)不应该的情况下抱怨:

SCC has been trained to handle 'q' single quotes in most of
the aberrant forms that can be used. '\0', '\', '\'', '\\
n' (a valid variant on '\n'), because the backslash followed
by newline is elided by the token scanning code in CPP before
any other processing occurs.

当 GCC 4.3.2 中的 CPP 处理此问题时,它会抱怨(警告):

SCC has been trained to handle 'q' single quotes in most of
<stdin>:2:56: warning: missing terminating ' character
the aberrant forms that can be used. '\0', '\', '\'', '\\
<stdin>:3:27: warning: missing terminating ' character
n' (a valid variant on '\n'), because the backslash followed
by newline is elided by the token scanning code in CPP before
any other processing occurs.

C99 标准的第 5.1.1.2 节翻译阶段说:

翻译语法规则的优先级由以下阶段指定。(脚注 5

  1. 物理源文件多字节字符以实​​现定义的方式映射
    方式,到源字符集(引入换行符
    行尾指示器)(如有必要)。 三字母序列被替换为
    相应的单字符内部表示。

  2. 每个反斜杠字符 () 后面紧跟一个换行符
    字符被删除,拼接物理源行以形成逻辑源行。
    只有任何物理源行上的最后一个反斜杠才有资格成为一部分
    这样的接头。 非空的源文件应以换行符结尾,
    在任何此类之前不得紧接反斜杠字符
    发生拼接。

脚注 5 是:

(5) 实现的行为应如同这些单独的阶段发生一样,即使
尽管在实践中许多通常折叠在一起。

因此,在我看来,CPP 对示例文本中的第二阶段处理不当。 或者,至少,警告不是我想要的 - 构造是有效的 C,并且警告是否合理并不是不言而喻的。

当然,这是一种边缘情况,并且允许额外的警告。 但这会惹恼我的生活。 如果我没有自己的、可能更好的工具来完成这项工作,那么使用 'cpp -fpreprocessed -P' 就可以了 - 这是我抱怨的极端边缘情况(而且,认为存在问题的可能性大于没有问题的可能性可能是合理的——尽管更好的启发式方法会观察到该行被拼接并且结果是合法的单字符常量,因此如果出现以下情况,则应抑制投诉; result 不是一个合法的单字符常量,那么应该产生投诉(在我的测试用例中 - 无可否认,这是一个酷刑测试 - CPP 产生了 13 个问题,大部分与我所抱怨的问题有关,其中 SCC 正确地产生了 2 个问题。)

(我观察到,“-P”设法抑制了省略该选项时出现的输出中的“#line”指令。)

I have a program called scc that strips C comments (and C++ comments, though with C99 they're the same). Apply that plus a filter to remove blank lines and, if so desired, lines containing just open and close braces, to generate the line counts. I've used that on internal projects - not needed to discount open/close braces. Those scripts were more complex, comparing the source code for two different versions of a substantial project stored in ClearCase. They also did statistics on files added and removed, and on lines added and removed from common files, etc.

Not counting braces makes quite a difference:

Black JL: co -q -p scc.c | scc | sed '/^[       ]*$/d' | wc -l
     208
Black JL: co -q -p scc.c | scc | sed '/^[       {}]*$/d' | wc -l
     144
Black JL: co -p -q scc.c | wc -l
     271
Black JL:

So, 144 lines under your rules; 208 counting open and close brace lines; 271 counting everything.

Lemme know if you want the code for scc (send email to first dot last at gmail dot com). It's 13 KB of gzipped tar file including man page, torture test, and some library files.


@litb commented that 'cpp -fpreprocessed -P file' handles stripping of
comments. It mostly does. However, when I run it on the stress test
for SCC, it complains when (in my opinion) it should not:

SCC has been trained to handle 'q' single quotes in most of
the aberrant forms that can be used. '\0', '\', '\'', '\\
n' (a valid variant on '\n'), because the backslash followed
by newline is elided by the token scanning code in CPP before
any other processing occurs.

When the CPP from GCC 4.3.2 processes this, it complains (warns):

SCC has been trained to handle 'q' single quotes in most of
<stdin>:2:56: warning: missing terminating ' character
the aberrant forms that can be used. '\0', '\', '\'', '\\
<stdin>:3:27: warning: missing terminating ' character
n' (a valid variant on '\n'), because the backslash followed
by newline is elided by the token scanning code in CPP before
any other processing occurs.

Section 5.1.1.2 Translation Phases of the C99 standard says:

The precedence among the syntax rules of translation is specified by the following phases.(Footnote 5)

  1. Physical source file multibyte characters are mapped, in an implementation-defined
    manner, to the source character set (introducing new-line characters for
    end-of-line indicators) if necessary. Trigraph sequences are replaced by
    corresponding single-character internal representations.

  2. Each instance of a backslash character () immediately followed by a new-line
    character is deleted, splicing physical source lines to form logical source lines.
    Only the last backslash on any physical source line shall be eligible for being part
    of such a splice. A source file that is not empty shall end in a new-line character,
    which shall not be immediately preceded by a backslash character before any such
    splicing takes place.

Footnote 5 is:

(5) Implementations shall behave as if these separate phases occur, even
though many are typically folded together in practice.

Consequently, in my view, CPP is mishandling phase two in the example text. Or, at least, the warning is not what I want - the construct is valid C and it is not self-evident that the warning is warranted.

Granted, it is an edge case, and extra warnings are permitted. But it would annoy the living daylights out of me. If I didn't have my own, possibly better tool for the job, then using 'cpp -fpreprocessed -P' would do - it is an extreme edge case that I'm complaining about (and, it might be legitimate to argue that it is more likely that there is a problem than not -- though a better heuristic would observe that the line was spliced and the result was a legitimate single character constant and therefore the complaint should be suppressed; if the result was not a legitimate single character constant, then the complaint should be produced. (On my test case - admittedly a torture test - CPP yields 13 problems, mostly related to the one I'm complaining about, where SCC correctly yields 2.)

(I observe that the '-P' manages to suppress a '#line' directive in the output that appears when the option is omitted.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文