Linux:统计文件中空格和其他字符的数量

3

问题:

我需要匹配邮寄机软件程序的确切格式。它期望特定的格式。我可以使用工具计算换行符、回车符、制表符等的数量。

cat -vte

而且
od -c

And(并且)
wc -l ( or wc -c )

然而,我想知道字符和文本段之间以及首尾空格的确切数量,包括制表符。

问题:

您会如何使用常见的Unix工具+Perl或Python分析并精确匹配模板?是否有一行代码可以完成?此外,您对于匹配DOS编码文件有什么建议?是先将其转换为NIX格式再进行分析,还是保留原样?

更新:

使用以下命令查看单个空格[假定文件中没有'%'字符]:

sed 's/ /%/g' filename.000

计划构建一个脚本,分析每行的制表符和空格内容。
使用 @shiplu 的解决方案,并向反对 cat 的人致敬。
while read l;do echo $l;echo $((`echo $l |  wc -c` - `echo $l | tr -d ' ' | wc -c`));done<filename.000

虽然在Windows上仍需要一些调整,但已经走上了正确的道路。

示例文本

阅读要点:

换行符标记为 \n

回车符标记为 \r

未知空格/制表符标记为 [:space:](需要计算次数)

\r\n
\n
[:space:]Institution Anon LLC\r\n
[:space:]123 Blankety St\r\n
[:space:]Greater Abyss, AK  99999\r\n
\n
\n
[:space:]                                10/27/2011\r\n
[:space:]Requested materials are available for pickup:\r\n
[:space:]e__\r[:space:]                     D_ \r[:space:]   _O\r\n
[:space:]Bathtime for BonZo[:space:]       45454545454545[:space:]  10/27/2011\r\n
[:space:]Bathtime for BonZo[:space:]       45454545454545[:space:]  10/27/2011\r\n
\n
\n
\n
\n
\n
\n
[:space:]                             Pantz McManliss\r\n
[:space:]                             Gibberish Ave\r\n
[:space:]                             Northern Mirkwood, ME  99999\r\n
( untold variable amounts of \n chars go here )

更新2

使用IFS和read命令可以得到类似于下面某人发布的ruby的结果。

while IFS='' read -r line
 do 
     printf "%s\n" "$line" | sed 's/ /%/g' | grep -o '%' | wc -w
 done < filename.000

你想要计算空格字符的数量吗? - Shiplu Mokaddim
添加的制表符和空格数量是相同还是不同?您有4-5个示例行+输出吗? - user unknown
@user unknown 当然,给我一秒钟。 - Bubnoff
换句话说,您想要计算每行中所有空格(不包括行尾的 -\n 或 \r\n-- 这是您所说的 DOS 编码吗?),并将此数字按行写入吗? - inger
正确。我没有显示任何制表符,但有许多空格。Ruby 和上面的最新 while 版本都可以实现这一点。不确定为什么 Ruby 和 Bash 会产生略微不同的结果,但现在几乎是时候开始使用打印机进行测试了。谢谢大家! - Bubnoff
显示剩余7条评论
7个回答

5
perl -nlE'say 0+( () = /\s/g );'

与目前接受的答案不同,此方法不会将输入分成字段并丢弃结果。它也不需要创建数组来计算列表中的值数。

使用的惯用语:

  • 0+( ... ) 强制上下文为标量上下文,就像 scalar( ... ),但清晰易懂,因为它告诉读者期望得到一个数字。
  • 在标量上下文中进行列表赋值会返回其右侧返回的元素数量,因此0+( () = /.../g ) 返回 () = /.../g 匹配的次数。
  • -n 一起使用时,-l 会导致输入“被咀嚼”,因此这会从计数中删除换行符。

如果您只对空格(U+0020)和制表符(U+0009)感兴趣,则以下方法更快且更简单:

perl -nE'say tr/ \t//;'

无论哪种情况,您都可以通过STDIN或由参数命名的文件传递输入。

不错!运行得很好。但仍然想知道为什么根据所使用的解决方案,结果会相差一两个。在这个项目的范围内,这并不重要,但仍然是一个好奇的侧面说明。 - Bubnoff
@Bubnoff,我怀疑有些行末包含CR和/或LF。我的代码将包括CR。为什么你的Unix文件中会有CR?首先使用“dos2unix”修复你的文件。 - ikegami
这听起来像是一个合理的解释。打印机需要DOS(或其他格式),如果可能的话,我不想对文件进行任何修改。我不想计算CR或LF。所以实际上,bash循环可能是最准确的,因为它只捕获空格?我找不到这个文件中的制表符,我认为它只是空格。 - Bubnoff
@Bubnoff,将\s替换为[ \t]就可以解决问题了,使用tr/ \t//解决方案也可以,因为它们专门寻找空格和制表符,而不是一般的空白字符。 - ikegami
perl -nE'say tr/ \t//;' 产生的结果与 bash 循环相同。 - Bubnoff
关于编程的内容:re: \s vs [ \t]。我真是个蠢货...我知道了,谢谢! - Bubnoff

4
Perl或Python中的正则表达式是解决这个问题的方法。 是的,可能需要一定时间的初步投资来学习“perl, schmerl, zwerl”,但一旦你掌握了像正则表达式这样极其强大的工具,它可以在以后节省你大量的时间。

enter image description here


1
我了解正则表达式。寻找一些单行代码。应该在一开始就澄清这一点。谢谢 XKCD。 - Bubnoff

2

统计空格:

sed 's/[^ ]//g' FILE | tr -d "\n" | wc -c

在文本之前、之后和之间。您想要一次性计算换行符、制表符等,并将它们加总,还是分别进行?


这非常接近我要达到的目标[请参见更新]。但需要计算每行的空格或制表符。您的tr位是一个有趣的解决方案。 - Bubnoff

2
perl -nwE 'print; for my $s (/([\t ]+)/g) { say "Count: ", length $s }' input.txt

这将会统计独立的制表符或空格组,而不是计算整行中所有空白字符的数量。例如:
    foo        bar

将会打印

    foo        bar
Count: 4
Count: 8

你可能希望跳过单个空格(单词之间的空格)。例如,不要计算 Bathtime for BonZo 中的空格。如果是这样,请将 + 替换为 {2,} 或任何你认为适当的最小值。


摇滚乐(到处都是重音符号)。我需要做一些测试,但我认为这可能是迄今为止最好的! - Bubnoff
@Bubnoff 如果我确切知道你想做什么,我可能可以提供更好的答案。但我想你可以根据自己的需要进行调整。 - TLP
我们正在迁移到一个生产与上述示例文本不同格式的新系统。我需要让新系统匹配上述格式--我正在分析预期格式,希望能在新系统中进行匹配。由于分析接近完成,我可能需要开始一个新问题,以确定现在最好的方法。 - Bubnoff

1

如果算上Ruby(确实):

ruby -lne 'puts scan(/\s/).size'

现在是一些Perl代码(个人认为不太直观):

perl -lne 'print scalar(@{[/(\s)/g]})'


简洁而美好。还有其他限制我完全不懂Ruby,但我会为满足其中一个要求的简洁投赞成票。我得去看看Ruby。谢谢! - Bubnoff
谢谢。添加了一些Perl代码……很抱歉没有满足所有要求……让我重新阅读一遍。 - inger
太棒了!这也可以工作。但是这里有一个难题。本页上的所有三个解决方案(bash、ruby、perl)的结果都相差一到两个。Perl 每行计算的空格比 bash 少一个,而某些行则比其他行多一些空格。我认为这将是一个相当手动的过程。 - Bubnoff
嗯,我必须承认我不确定我完全理解您的要求 - 其他人可能也不理解。所以,为了澄清:您只需要每行一个数字:忽略行尾的空格数。上述两个Ruby / Perl 1-liners似乎都满足这一点。还有其他微妙之处/要求/限制吗?运行bash代码对您的示例进行转换似乎也会将行转储,并剥离前导空格(这是要求吗?)。这里的Ruby和Perl针对问题中的示例给出了相同的结果。您在那里看到了什么区别? - inger
如果您查看最后一个bash示例,它会保留空格并给出类似的结果。Ruby和Perl同样有效,但是所有三个在不同行上都会给出稍微不同的结果。就好像所有三个翻译空格略有不同。所有这些都足以完成项目...我只是想知道为什么会有稍微不同的结果。 - Bubnoff
是的,我使用了\s,它表示“任何空白字符”,而 Bash 3 行脚本似乎只寻找空格? - inger

1
如果你想要计算在pm.txt中的空格数量,可以使用以下命令:
 cat pm.txt | while read l; 
 do echo $((`echo $l |  wc -c` - `echo $l | tr -d ' ' | wc -c`));
 done;

如果你想计算空格、\r、\n、\t的数量,请使用以下代码:
cat pm.txt | while read l;
do echo $((`echo $l |  wc -c` - `echo $l | tr -d ' \r\n\t' | wc -c`));
done;

read会去掉任何前导字符。如果你不想这样,有一种不好的方法。首先使用

将文件拆分,使每个文件只有1行。
`split -l 1 -d pm.txt`. 

之后会有一堆x*文件。现在循环遍历它。

for x in x*; do echo $((`cat $x |  wc -c` - `cat $x | tr -d ' \r\n\t' | wc -c`)); done;

通过rm x*命令删除这些文件;


我可以听到“反使用猫”的人群在哀叹和咬牙切齿,但我认为这是正确的方向。不过,我需要逐行分析。 - Bubnoff
太厉害了...我正在进行我的实验,但你比我更快。这就是了。我只需要在中间部分玩一下将其从 DOS 转换为 UNIX,然后就完成了。谢谢,老兄! - Bubnoff
while循环在某种程度上剥离了前导空格。在包含多达50个空格的行中,仅显示3个空格。 - Bubnoff
如果我进行逐行分析而没有使用循环,它们会显示。例如,grep "regex" -m 1 bkct.000 | sed 's/ /%/g' | grep -o '%' | wc -w - Bubnoff
“# while IFS='' read -r line” 修复了此问题。在此处找到:https://dev59.com/GUrSa4cB1Zd3GeqPTA2j - Bubnoff

-1
如果你问我,我会写一个简单的C程序来完成计数和格式化。但这只是我的想法。如果我用Perl、Schmerl、Zwerl等浪费半天时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接