我需要按字符 CLASS(由 BASH 手册页定义)有效地计算任意文件的每个字符; IE
[[:alnum:]]、[[:alpha:]]、[[:ascii:]]、[[:blank:]]、[[:cntrl:]]、[[:digit:]]、[[ :图:]]、[[:下:]]、[[:打印:]]、[[:punct:]]、[[:空格:]]、[[:上:]]、[[:字:]] 和 [[:xdigit:]]
处理文件后,在单行上显示每个结果的计数,即使为零。
网络搜索并没有成功地找到类似的内容。
任意文件 ( /tmp/f1.txt
) 将包含各种不同的文本/数据。
我不想处理 ELF 二进制文件或 Unicode(或任何形式的多字节)内容。
我不关心行数(CR
和/或LF
),只关注通过上述类累积目标文件中每个“字符”的计数。
我打算让它最终成为function()
更大的 bash 脚本中的标准。需要bash/sed/awk等;而 perl/python/ruby 则没有那么多。
示例数据文件可以是:
零字节,即根本没有内容。
单个字符
一句话
多个单词之间用空格分隔
多行中散布着空格和/或回车符和/或换行符。
对于多行文件,可能没有
CR
或LF
来表示最后一行的结尾(但仍应计算所有字符)。
答案1
file=myfile
for class in alnum alpha blank cntrl digit graph lower print punct space upper xdigit
do
printf '%7s: %d\n' "$class" "$(tr -Cd "[:${class}:]" < "$file" | wc -m)"
done
ascii
和word
不是标准字符类并且是bash
特定的。word
是alnum
加下划线,并且ascii
是字符0到127,所以你可以这样做:
printf '%7s: %d\n' word "$(tr -Cd "_[:alnum:]" < "$file" | wc -m)"
printf '%7s: %d\n' ascii "$(LC_ALL=C tr -cd '\0-\177' < "$file" | wc -c)"
(请注意,从 coreutils-8.22 开始, 的 GNU 实现tr
无法使用多字节字符)。
至少在使用 GNU libc 的系统上,您还可以运行:
$ locale ctype-class-names
upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum;combining;combining_level3
查找您的语言环境中定义的字符类列表。
答案2
听起来像是一堂有趣的课!它是什么?
这将帮助您完成大部分工作; sed 似乎不支持 :ascii: 或 :word:,但是:
for f in alnum alpha ascii blank cntrl digit graph lower print punct space upper word xdigit
do
echo "$f: $(sed s/[^[:$f:]]//g b.txt | tr -d '\n' | wc -c)"
done
我们使用 sed 来删除所有内容除了我们关心的字符,然后删除所有空白行,并获取剩余的字符数。
应相对准确,但可能存在 +/-1 或十倍误差的例外。
答案3
我不会查找哪些角色适合哪个类别 - 也许您可以弄清楚和/或只需查阅其他答案。但这将为您提供文件字符的明确表示,而不会丢失任何字符:
_c2o() { od -A n -t o1 -w1 -v | tr -dc '0-9\n' ; }
_c2o <file
163
150
072
040
167
141
162
156
151
156
147
我以很多不同的方式使用这个功能。每行都是以八进制格式表示的一个字节 - 尽管od
是非常可配置的。但通过这种方式,您可以非常轻松地仅grep
或sed
为您的目标值并实现行计数器。这真是小菜一碟。而且速度非常快。
好吧,所以我还是继续上课了:
_classes() { set -- ${classes=alnum alpha blank cntrl digit graph lower print punct space upper xdigit}
while ${1+:} false ; do
printf %b $(printf '\\%04o\n' $(seq 0 127)) |
tr -dc "[:${1}:]" | {
printf "$1='"
_c2o
printf "'\n"
} ; shift
done
}
运行上面的命令,你会得到如下输出:
xdigit='060
061
062
063
064
065
066
067
070
...
'
从那里,我会想象这样的事情:
eval "$(_classes)"
for class in $classes ; do
eval "$class=\$(_c2o <file | grep -c -F "$class")"
done
我需要更好地解决这个问题——但这就完成了全部工作。