shell 如何计算代码中的单词或标记?

plicqrtu  于 2023-03-03  发布在  Shell
关注(0)|答案(2)|浏览(119)

有各种各样的工具可以用来计算源文件或目录树(例如cloc)中的代码行数,也有一些工具可以用来计算纯文本文件(wc)中的字数。
但是,我该如何计算代码中的单词或标记呢?如果不编写一个成熟的程序,使用一些通用的编程语言解析机制(如tree-sitter),这是否可行呢?更具体地说,我可以使用shell工具或简单的脚本来完成吗?
注意:只有注解以外的单词/标记必须计算在内。对于一般的单词计数,我肯定还有其他问题...
示例:假设我的代码是用C语言编写的,并且foo.c文件包含

int /* this is
a multi-line
comment!
*/
foo(int x) { 
    /* comment 1 */
    return 123;  // comment 2
}

这里期望的确切数目取决于我们是否将大括号和分号视为要计数的单词/标记。如果我们这样做,那么这应该是11个标记:intfoo(intx){return123;}.如果我们忽略它们(我不想这样,但这仍然可能是一个合法的方法),那么我们有6个词:一米十四个一英寸、一米十五个一英寸、一米十六个一英寸、一米十七个一英寸、一米十八个一英寸、一米十九个一英寸。

db2dz4w8

db2dz4w81#

    • 每行的非注解标记总数**

编辑,我的错,我离开了@Gilles示例,错过了注解部分。根据您使用C/C ++注解并忽略/**/之间的多行注解的示例,每行非注解标记可以使用计数器tokens和标志skip通过检查字段是否包含在"//"上来获得。"/*""*/",因为您会在每个标记周围显示空白。将文件处理为非注解空白分隔标记的简单awk脚本可以是:

#!/bin/awk -f

{
  tokens = 0
  skip = 0
  for (i=1; i<=NF; i++) {
    if ($i == "//") {
      break
    }
    if ($i == "/*") {
      skip = 1
    }
    if (!skip) {
      tokens++
    }
    if ($i == "*/") {
      skip = 0
    }
  }
  printf "line %d: %d tokens\n", FNR, tokens
}

(note:解析C语言中包含非witespace的单个标记,例如"foo(int"未被寻址。如果需要在该级别进行解析,则使用awk重新创建轮子可能不是您的最佳选择。但是添加条件以忽略仅由(,{,[],},)组成的字段是很容易做到的。)
单个规则迭代每个字段并检查开始注解。对于"//",该行的其余部分将被忽略。对于"/*",将设置skip标志,并且在该行中遇到结束"*/"之前不再计数标记。

    • 使用/输出示例**

修改的示例文件:

$ cat file
foo bar // base base
lorem ipsum doloris
qux /* aze */ qwe base

如果你把你的awk脚本命名为noncmttokens.awk,并使用chmod +x noncmttokens.awk使其可执行,那么你所需要做的就是把file作为参数来运行它,例如:

$ ./noncmttokens.awk file
line 1: 2 tokens
line 2: 3 tokens
line 3: 3 tokens

很抱歉忽略了问题中的评论赘言,我使用了另一个答案中的示例文件--发生了...

    • 在"("上添加多行注解处理和split**

要将文件处理为所需的标记,同时保持所有注解打开/关闭都是空格分隔的,并且只在"("上拆分非空格分隔的标记,您可以执行以下操作:

#!/bin/awk -f

BEGIN {
  tokens_in_file = 0    # initialize vars that are persistent across records
  skip = 0
}

{
  tokens_in_line = 0;   # per-record reset of varaibles
  ndx = 1
}

skip {  # if in muli-line comment
  for (ndx=1; ndx<=NF; ndx++) {   # iterate fields
    if ($ndx == "*/") {           # check for multi-line close
      skip = 0;                   # unset skip flag
      ndx++                       # increment field index
      break
    }
  }
  if (skip) {   # still in multi-line comment
    ndx = 1
    printf "line %d: %d tokens\n", FNR, tokens_in_line
    next
  }
}

{
  for (i=ndx; i<=NF; i++) {   # process fields from ndx to last
    if ($i ~/^[({})]$/) {     # ignore "(, {, }, )" fields
      continue
    }
    if ($i == "//") {         # C++ rest of line comment
      break
    }
    if ($i == "/*") {         # multi-line opening
      if (skip) {             # handle malformed multi-line error
        print "error: duplicate milti-line comment entry tokens" 
      }
      skip = 1                # set skip flag
    }
    if (!skip) {              # if not skip, process toks, split on "("
      tokens_in_line += split ($i, tok_arr, "(")
    }
    if ($i == "*/") {         # check if last field multi-line close
      skip = 0
    }
  }
  # output per-line stats, add tokens_in_line to tokens_in_file
  printf "line %d: %d tokens\n", FNR, tokens_in_line
  tokens_in_file += tokens_in_line
}

END { # output file stats
  printf "\nindentified %d tokens in %d lines\n", tokens_in_file, FNR
}
    • 使用/输出示例**

使用file2.c中提供的示例文件,例如

$ cat file2.c
int /* this is
a multi-line
comment!
*/
foo(int x) {
    /* comment 1 */
    return 123;  // comment 2
}

将该文件作为参数提供给扩展的awk脚本,您将得到:

$ ./noncmttokens2.awk file2.c
line 1: 1 tokens
line 2: 0 tokens
line 3: 0 tokens
line 4: 0 tokens
line 5: 3 tokens
line 6: 0 tokens
line 7: 2 tokens
line 8: 0 tokens

indentified 6 tokens in 8 lines

awk可以高效地处理您需要做的任何事情,但正如评论中提到的,我怀疑随着更多细节的添加,它将成为一项更多的工作,重新发明编译器在其编译级别之一所做的事情。这种标记的拆分是基本的,但需要处理的极端情况的数量,例如处理混淆的C/C ++代码的需求迅速地呈指数增长。
希望这能提供你所需要的。

qpgpyjmq

qpgpyjmq2#

文件

$ cat file
foo bar base base
lorem ipsum doloris
qux aze qwe base
考虑下面这个简单的perl代码片段:
$ perl -snE '$c += s/\bbase\b/$&/g;END{say $c}' file
3
使用bash
for word in $(< file); do
    [[ $word == base ]] && ((c++))
done
echo "$c"
使用grep
printf '%s\n' $(< file) | grep -wc base
使用awk
tr ' ' $'\n' < file | awk '$1=="base"{c++}END{print c}'

相关问题