计算宽度超过 80 列的行数,正确考虑制表符

计算宽度超过 80 列的行数,正确考虑制表符

目前,要计算宽度超过 80 列的行,我使用以下命令:

$ git grep -h -c -v '^.\{,80\}$' **/*.{c,h,p{l,y}} \
    |awk 'BEGIN { i=0 } { i+=$1 } END { printf ("%d\n", i) }'
44984

不幸的是,该存储库使用制表符进行缩进,因此grep模式不准确。无论如何,是否有regex像 8 个字符的标准宽度一样的处理选项卡wc -L

出于这个问题的目的,我们可以假设贡献者有足够的纪律来一致缩进,或者他们有git commit钩子代替纪律。

出于与性能相关的原因,我更喜欢在内部工作的解决方案 git-grep(1)或其他grep工具,无需预处理文件

答案1

通过管道传输文件来预处理文件expand。该expand实用程序将适当地扩展制表符(在每 8 个字符处使用标准制表符停止位)。

find . -type f \( -name '*.[ch]' -o -name '*.p[ly]' \) -exec expand {} + |
awk 'length > 80 { n++ } END { print n }'

答案2

GNUwc -L不会将 TAB 视为 8 个字符,而是将 TAB 视为在终端中显示,每 8 列有一个 TAB 停止位,因此“宽度”范围为 1 到 8 个字符,具体取决于它们在行中的位置。wc -L还考虑其他字符的显示宽度(无论它们是 0、1 还是 2 列宽)并“正确”处理\f和。\r

$ printf 'abcde\t\n' | wc -L
8

在这里,您可以使用expand(默认情况下也假设每 8 列制表符停止一次,尽管您可以使用选项更改它)将这些制表符扩展为空格:

git grep -h '' ./**/*.{c,h,p{l,y}} | expand | tr '\f\r' '\n\n' | grep -cE '.{81}'

(将 CR(发送到终端时将光标移回行首)和 FF(某些显示设备将其理解为分页符)转换为 LF 以获得与 相同的行为,wc -L但忽略其他行为无论如何,我们无法判断它们会对显示宽度产生什么影响)。

这涵盖了制表符,但不包括单角或双角字符。请注意expand,如果存在多字节字符(更不用说零宽度或双宽度字符),当前的GNU 实现无法正确扩展制表符。

$ printf 'ééééé\t\n' | wc -L
8
$ printf 'ééééé\t\n' | expand | wc -L
11

还请注意,默认情况下会跳过隐藏文件或隐藏目录中的文件。由于括号扩展会扩展到多个 glob,因此如果其中一个 glob 不匹配,./**/*.{c,h,p{l,y}}您也会收到错误(使用zsh或会导致严重错误)。bash -O failglob

对于zsh,您可以使用./**/*.(c|h|p[ly])(D.)which isglob,其中D包含隐藏文件并.限制常规的文件。

对于考虑字符实际宽度的解决方案(假设所有文本文件都以区域设置的字符编码进行编码),您可以使用:

git grep -h '' ./**/*.(c|h|p[ly])(.) | tr '\r\f' '\n\n' |
  perl -Mopen=locale -MText::Tabs -MText::CharWidth=mbswidth -lne '
    $n++ if mbswidth(expand($_)) > 80;
    END{print 0+$n}'

请注意,至少在 GNU 系统上,mbswidth()将控制字符视为宽度为-1且 1 为expand()。我们假设文件中没有找到除 CR、NL、TAB、FF 之外的控制字符。

答案3

如果我们可以根据你的评论假设标签字符只会出现在行首,那么我们可以将替代字符计算为至少 80 个字符。

  • 无制表符,至少 81 个字符
  • 一个选项卡,至少 73 个字符
  • 两个选项卡,至少 65 个字符
  • ETC。

由此产生的混乱如下,您的awk语句将各个行计数相加以提供总计

git grep -hcP '^(.{81,}|\t.{73,}|\t{2}.{65,}|\t{3}.{57,}|\t{4}.{49,}|\t{5}.{41,}|\t{6}.{33,}|\t{7}.{25,}|\t{8}.{17,}|\t{9}.{9,}|\t{10}.)' **/*.{c,h,p{l,y}} |
    awk '{ i+=$1 } END { printf ("%d\n", i) }'

答案4

解决方案与前任(从)。虽然很慢。

由于 vi 能够正确处理 UTF-8 数据:

它可以将制表符扩展为空格,将控制字符计为 1,\r \t \f \v正确处理并处理大多数有效的UNICODE 值。包括组合重音 (NKC) 和分解重音 (NKD),以及来自西里尔文、阿拉伯文、希腊文、中文等的字符。

$ cat script.sh
#!/bin/bash --

declare -i count=0

for i do
    # Set ex script in one variable
    a='set expandtab        "       Expand tabs to spaces
       r '"$i"'             "       Read original file
       g/^.\{,80\}$/d       "       Remove all lines shorter than the value used
       wq                   "       Quit ' 

    o=outfile; :>"$o"           # Clean output file
    ex -s "$o" <<<"$a"          # process lines in $i file
    count+=$(wc -l <"$o")       # count and accumulate number of lines.
done

echo "$count"

调用脚本为:

$ script.sh     **/*.{c,h,p{l,y}}
44984

相关内容