【问题标题】:Simple script to count NLOC?计算NLOC的简单脚本?
【发布时间】:2008-11-27 10:28:08
【问题描述】:

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

【问题讨论】:

    标签: c++ c metrics lines-of-code


    【解决方案1】:

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

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

    如果您想包含 cmets:

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

    【讨论】:

      【解决方案2】:

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

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

      我可以扩展它来处理行 cmets:

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

      或许

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

      处理块 cmets 稍微复杂一些(我不是 Perl 专家!)。

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

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

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

      【讨论】:

      • 我认为“non-commented”是缩写的来源,但是当你开始考虑它时,还有其他事情你也不想算作额外的行,例如函数返回在自己的行上键入。这就是为什么我认为“netto”更精确(虽然更难以捉摸)。
      • 别忘了:你的答案是我一直在寻找的方向。谢谢,菲洛
      【解决方案3】:

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

      【讨论】:

      • 谢谢,蒂姆。对于这个项目,我正在寻找可以从命令行调用的东西。我们使用 gcc 进行编译。
      【解决方案4】:

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

      【讨论】:

      • 这似乎是一个 C# 应用程序(源文件扩展名无论如何都是“.cs”),这限制了它在 Windows 平台上的可用性。
      • 它的网站说它也可以在单声道上运行
      【解决方案5】:

      Source monitor 是免费的源码分析软件。它是 Windows 应用程序,但也可以使用命令行中的参数运行。

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

      【讨论】:

        【解决方案6】:

        Ohloh 提供免费的Ohcount,它计算代码行数和 cmets。

        【讨论】:

        • 请注意,这需要 Ruby(如果您要构建它,还需要系统上的各种其他零碎物品)。这并不是说它没有用 - 只是不是每个人都能轻松使用它。
        【解决方案7】:

        如果cmets还能进去,标准的unix工具就足够了:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            我通常只是这样做:

            grep -vc '^$' (my files)
            

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

            【讨论】:

              【解决方案10】:

              Locmetrics 效果很好。

              【讨论】:

              • 我尝试的第一个无需配置即可开箱即用。
              【解决方案11】:

              这是一个简单的 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
              

              【讨论】:

                【解决方案12】:

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

                #脚本开始

                var str 文件
                var str 目录

                set $files = "*.cpp" # set $dir = "C:/myproject" #

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

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

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

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

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

                # 逐个遍历 $fileList。
                而 ($fileList"")

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

                # 检查这是否是一个平面文件。我们对目录不感兴趣。
                af $file >null # 我们不想看到输出。
                # 我们只想设置 $ftype 变量。
                如果 ($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>
                

                完成
                结束

                完成

                显示总和

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

                #脚本结束

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

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

                【讨论】:

                  【解决方案13】:

                  不是一个简单的脚本,但 CCCC(C 和 C++ 代码计数器)已经存在了一段时间,它对我来说非常有用。

                  【讨论】:

                    【解决方案14】:

                    我有一个名为scc 的程序,它剥离了 C cmets(和 C++ cmets,尽管在 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 的第一个 dot last)。它是 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”就可以了——这是我抱怨的极端情况(而且,争辩说它是更有可能存在问题 - 尽管更好的启发式方法会观察到该行被拼接并且结果是合法的单字符常量,因此应该抑制投诉;如果结果不是合法的单字符常量,然后应该产生投诉。(在我的测试案例中 - 诚然是酷刑测试 - CPP 产生 13 个问题,主要与我所抱怨的问题有关,其中 SCC 正确产生 2。)

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-01-27
                      • 1970-01-01
                      • 2019-01-08
                      • 2012-09-05
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多