我想知道如何通过某些文本处理实用程序来计算每行中特定字符的数量吗?

例如,要计算以下文本的每行中的"

"hello!" 
Thank you!


第一行有两行,第二行有0。

评论

只需补充一点,您可以为此编写自己的10行C程序,而不是使用带有sed的正则表达式来提高性能。您应该考虑根据输入文件的大小进行操作。

#1 楼

您可以使用sedawk做到这一点:

$ sed 's/[^"]//g' dat | awk '{ print length }'
2
0


其中dat是您的示例文本,sed删除(每行)所有非"字符,并且awk分别打印行其大小(即length等效于length((),其中sed表示当前行)。例如,对于tr到:

's/[^(]//g'


更新:tr对于这项任务来说有点过头了-tr足够。与-c等效的解决方案是:

$ tr -d -c '"\n' < dat | awk '{ print length; }'


意味着"\n删除了字符集q4312079q中所有不是字符的字符(q4312079q表示补码)。

评论


+1应该比tr&wc版本更有效。

–StéphaneGimenez
11年8月14日在19:41

是的,但是它可以处理Unicode吗?

–amphetamachine
11年8月15日在10:51

@amphetamachine,是的-在Ubuntu 10.04系统上,至少使用ß(utf十六进制:c3 9f)(而不是“”)进行快速测试可以按预期工作,即tr,sed和awk可以毫无问题地进行补码/替换/计数。

–maxschlepzig
2011年8月15日在18:29

包括GNU tr和经典Unix tr在内的大多数tr版本均以单字节字符运行,并且不兼容Unicode。引自Wikipedia tr(Unix)..请尝试以下代码段:echo“aā⧾c” | tr“ā⧾” b ...在Ubuntu 10.04上...ß是单字节扩展拉丁字符,由tr处理...此处的真正问题不是tr不处理Unicode(因为所有字符都是Unicode) ),实际上tr一次只能处理一个字节。

– Peter.O
2011年8月15日在19:32



@fred,不,ß不是单字节字符-它的Unicode位置是U + 00DF,在UTF-8中编码为“ c3 9f”,即两个字节。

–maxschlepzig
11年8月16日在7:20

#2 楼

我只需要使用awk

awk -F\" '{print NF-1}' <fileName>


这里我们将字段分隔符(带有-F标志)设置为字符",那么我们要做的就是打印字段数NF -1.目标字符的出现次数将比分隔字段的数目少一。

对于由Shell解释的有趣字符,您只需要确保将其转义即可,否则命令行将尝试对它们进行解释。因此,对于"),都需要转义字段分隔符(带有\)。

评论


也许编辑答案以使用单引号引起来。它可以与任何字符一起使用('除外)。此外,它具有空行的奇怪行为。

–StéphaneGimenez
2011年8月15日在16:08



该问题专门使用“,因此我有义务使代码与之配合使用。这取决于您使用的是哪种外壳,天气需要转义字符,但bash / tcsh都需要转义”

–马丁·约克
2011年8月15日在16:10



当然,但是-F'“'没有问题。

–StéphaneGimenez
2011年8月15日在16:12



+1使用FS的好主意。...这将解决显示-1的空白行,例如bash命令行中的“ $ 1”。 ... awk -F“ $ 1”'{print NF == 0?NF:NF-1}'文件名

– Peter.O
11年8月15日在22:19

也可以使用多个字符作为分隔符...有用!

–线圈
16-09-30在15:35



#3 楼

使用tr和ard wc



 function countchar()
{
    while IFS= read -r i; do printf "%s" "$i" | tr -dc "" | wc -m; done
}
 


用法:

 $ countchar '"' <file.txt  #returns one count per line of file.txt
1
3
0

$ countchar ')'           #will count parenthesis from stdin
$ countchar '0123456789'  #will count numbers from stdin
 


评论


注意。 tr不处理使用多个字节的字符。.请参阅Wikipedia tr(Unix)..即tr不符合Unicode。

– Peter.O
2011年8月15日在19:43



您正在为文件的每一行运行4个命令

–StéphaneChazelas
2015年3月3日18:25



您需要从$ IFS中删除空格字符,否则read将从头到尾修剪它们。

–StéphaneChazelas
15年3月3日在18:25

你不能对任意数据使用echo

–StéphaneChazelas
15年3月3日在18:27

@ Peter.O,某些tr实现支持多字节字符,但是wc -c总是计数字节,而不是字符(字符需要wc -m)。

–StéphaneChazelas
15年3月3日在18:28

#4 楼

如果匹配数目太大(这正是我的情况),则使用awk的答案将失败。对于loki-astari的答案,报告了以下错误:

awk -F" '{print NF-1}' foo.txt 
awk: program limit exceeded: maximum number of fields size=32767
    FILENAME="foo.txt" FNR=1 NR=1


对于enzotib的答案(以及来自manatwork的等效结果),发生了分段错误: br />
awk '{ gsub("[^\"]", ""); print length }' foo.txt
Segmentation fault


maxschlepzig的sed解决方案可以正常工作,但是速度较慢(下面的时序)。

此处尚未建议某些解决方案。首先,使用grep

grep -o \" foo.txt | wc -w


,然后使用perl

perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt

解决方案中(从最慢到最快的顺序);我把事情限制在这里。 “ foo.txt”是一个包含一行和一个长字符串的文件,其中包含84922个匹配项。

## sed solution by [maxschlepzig]
$ time sed 's/[^"]//g' foo.txt | awk '{ print length }'
84922
real    0m1.207s
user    0m1.192s
sys     0m0.008s

## using grep
$ time grep -o \" foo.txt | wc -w
84922
real    0m0.109s
user    0m0.100s
sys     0m0.012s

## using perl
$ time perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt
84922
real    0m0.034s
user    0m0.028s
sys     0m0.004s

## the winner: updated tr solution by [maxschlepzig]
$ time tr -d -c '\"\n' < foo.txt |  awk '{ print length }'
84922
real    0m0.016s
user    0m0.012s
sys     0m0.004s


评论


+好主意!我在新的答案中扩展了表格,可以随时进行编辑(最终图片不太清楚,但是我相信@maxschlepzig是更快的解决方案)

– JJoao
2015年3月4日在8:35

maxschlepzig的解决方案超级快!

– petertc
16年4月1日在6:36

#5 楼

bashzshyashksh的某些实现/版本中的另一种不依赖外部程序的实现:

>

使用while IFS= read -r line; do line="${line//[!\"]/}" echo "${#line}" done <input-file 计数line="${line//[!(]}"

评论


当最后一行没有尾随\ n时,while循环退出,因为尽管它读取了最后一行,但它还返回了一个非零的退出代码来表示EOF ...绕过它,下面的代码片段可以正常工作(..这已经困扰了我一段时间了,我才发现这个工作错误)... eof = false; IFS =;直到$ eof;读-r || eof = true;回显“ $ REPLY”;做完了

– Peter.O
2011年8月15日在21:42



@Gilles:您添加了bash不需要的尾随/。这是ksh的要求吗?

– Enzotib
11年8月16日在7:35

在较旧的ksh版本中需要尾随/,在较旧的bash版本中也需要IIRC。

–吉尔斯'所以-不再是邪恶的'
11年8月16日在8:15

#6 楼

另一个awk解决方案:
awk '{print gsub(/"/, "")}' <filename>


#7 楼

使用awk和gsub的另一种可能的实现:

awk '{ gsub("[^\"]", ""); print length }' input-file


功能gsub与sed的's///g'等效。

使用gsub("[^(]", "")来计数(。 br />

评论


您可以保存一个字符,即在删除标准输入重定向...时)

–maxschlepzig
11年8月14日在20:34

@maxschlepzig:是的,当然;)

– Enzotib
11年8月14日在20:43

awk'{print gsub(/“ /,”“)}')输入文件就足够了,因为“对于与字符串t中的正则表达式r匹配的每个子字符串,替换字符串s,并返回替换数目。” (老兄)

–manatwork
2011年9月6日在12:42



#8 楼

我决定写一个C程序,因为我很无聊。 prettyprint-override“> #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char c = argv[1][0]; char * line = NULL; size_t len = 0; while (getline(&line, &len, stdin) != -1) { int count = 0; char * s = line; while (*s) if(*s++ == c) count++; printf("%d\n",count); } if(line) free(line); }

评论


谢谢!感谢您的无聊,以便我能学到一些东西。哦,等等,您需要退货吗?

– Tim
2011年8月14日23:31



*耸耸肩*,如果您想完全正确,还需要添加一些#include,但是编译器上的默认警告似乎并不在乎。

–user606723
2011年8月14日23:39

您可以省去free(line),因为退出程序会隐式释放所有分配的内存-然后有一个返回0的地方; ...;)。即使在示例中,将返回码保持未定义也不是好方法。顺便说一句,getline是GNU扩展-万一有人想知道。

–maxschlepzig
2011年8月15日在6:04

@maxschlepzig:内存是由getline()分配的行所指向的吗?它是通过malloc在堆上动态分配还是在堆栈上静态分配?您说释放它不是必需的,所以它不是动态分配的吗?

– Tim
2011年8月15日在6:28



@Tim,是的,例如如果您将代码重构为一个独立的函数-例如-f,它被其他代码多次调用,则必须在函数f的最后一次getline调用之后调用free。

–maxschlepzig
2011年8月15日在7:44

#9 楼

对于字符串,最简单的方法是trwc(无需使用awksed来消除过多的麻烦)-但请注意有关tr的上述注释,计数字节,而不是字符-

echo $x | tr -d -c '"' | wc -m


其中$x是包含要评估的字符串(不是文件)的变量。

#10 楼

这是另一个只需要STD C和更少内存的C解决方案:

 #include <stdio.h>

int main(int argc, char **argv)
{
  if (argc < 2 || !*argv[1]) {
    puts("Argument missing.");
    return 1;
  }
  char c = *argv[1], x = 0;
  size_t count = 0;
  while ((x = getc(stdin)) != EOF)
    if (x == '\n') {
      printf("%zd\n", count);
      count = 0;
    } else if (x == c)
      ++count;
  return 0;
}
 


评论


如果末尾没有'\ n',则不会在最后一行报告

– Peter.O
2011年8月15日在22:24



@fred,是的,这是有目的的,因为没有尾随\ n的行不是真实行。这与我的其他sed / awk(tr / awk)答案相同。

–maxschlepzig
11年8月16日在7:25

#11 楼

我们可以将grepregex结合使用,以使其更加简单和强大。

要计算特定字符。

$ grep -o '"' file.txt|wc -l


要计算特殊字符(包括空格字符)。 >
这里我们选择带有[\S\s]的任何字符,并带有-o选项,我们使grep在单独的行中打印每个匹配项(即每个字符)。然后使用wc -l对每一行进行计数。

评论


OP不想打印文件中所有字符的数量!他想计算/打印特定字符的数量。例如,每行中有多少个“”;对于其他任何字符,请参见他的问题并接受答案。

–αғsнιη
2014年11月23日19:14

#12 楼

也许一个更直接的答案,纯粹是awk的答案是使用split。
Split接受一个字符串并将其转换成数组,返回值是生成的数组项的数量+ 1。 >以下代码将打印出出现在每行上的次数。

awk ' {print (split(q4312078q,a,"\"")-1) }' file_to_parse


有关拆分的更多信息http://www.staff.science.uu。 nl /〜oostr102 / docs / nawk / nawk_92.html

#13 楼

对于纯bash解决方案(但是,它是bash特定的):如果$x是包含您的字符串的变量:

x2="${x//[^\"]/}"
echo ${#x2}


${x//会删除除"${#x2}之外的所有字符

(使用expr的原始建议有问题,请参阅注释:)

expr length "${x//[^\"]/}"


评论


请注意,它特定于GNU expr,并且计数字节,而不是字符。与其他expr:expr“ x $ {x ...}”:“ x。*”-1

–StéphaneChazelas
2014年11月23日下午21:27

哦,对了,谢谢!我已经用另一个想法修改了它,它的优点是根本不用外部程序。

–玛丽安
15年3月4日在23:08

#14 楼

提出的解决方案的时间比较(不是答案)

答案的效率并不重要。
尽管如此,按照@josephwb的方法,我试图安排所有答案的时间。

我用Victor Hugo“ Les Miserables”(很棒的书!)的葡萄牙语翻译作为输入,并计算“ a”的出现。我的版本有5卷,很多页...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 


C答案是使用gcc编译的(无优化)。

每个答案被运行了3次并选择最佳。

不要太相信这些数字(我的
机器正在执行其他任务,等等,等等)。我与您分享这些时间,因为我得到了一些意外的结果,并且我相信您会发现更多的结果。


16个定时解决方案中的14个花费的时间少于1秒;不到0.1s少了9个,其中许多使用管道
2个解决方案,逐行bash,通过创建新流程来处理了30k行,
在10s / 20s中计算出正确的解决方案。
grep -oP a的树时间比grep -o a快10倍(11; 12)。 (7; 8 vs 2; 3)
(欢迎结论)

(结果随机排列)

=========================1 maxschlepzig
$ time sed 's/[^a]//g' mis.txt | awk '{print length}' > a2
real    0m0.704s ; user 0m0.716s
=========================2 maxschlepzig
$ time tr -d -c 'a\n' < mis.txt | awk '{ print length; }' > a12
real    0m0.022s ; user 0m0.028s
=========================3 jjoao
$ time perl -nE 'say y!a!!' mis.txt  > a1
real    0m0.032s ; user 0m0.028s
=========================4 Stéphane Gimenez
$ function countchar(){while read -r i; do echo "$i"|tr -dc ""|wc -c; done }

$ time countchar "a"  < mis.txt > a3
real    0m27.990s ; user    0m3.132s
=========================5 Loki Astari
$ time awk -Fa '{print NF-1}' mis.txt > a4
real    0m0.064s ; user 0m0.060s
Error : several -1
=========================6 enzotib
$ time awk '{ gsub("[^a]", ""); print length }' mis.txt > a5
real    0m0.781s ; user 0m0.780s
=========================7 user606723
#include <stdio.h> #include <string.h> // int main(int argc, char *argv[]) ...  if(line) free(line); }

$ time a.out a < mis.txt > a6
real    0m0.024s ; user 0m0.020s
=========================8 maxschlepzig
#include <stdio.h> // int main(int argc, char **argv){if (argc < 2 || !*argv[1]) { ...  return 0; }

$ time a.out a < mis.txt > a7
real    0m0.028s ; user 0m0.024s
=========================9 Stéphane Chazelas
$ time awk '{print gsub(/a/, "")}'< mis.txt > a8
real    0m0.053s ; user 0m0.048s
=========================10 josephwb count total
$ time grep -o a < mis.txt | wc -w > a9
real    0m0.131s ; user 0m0.148s
=========================11 Kannan Mohan count total
$ time grep -o 'a' mis.txt | wc -l > a15
real    0m0.128s ; user 0m0.124s
=========================12 Kannan Mohan count total
$ time grep -oP 'a' mis.txt | wc -l > a16
real    0m0.047s ; user 0m0.044s
=========================13 josephwb Count total
$ time perl -ne '$x+=s/a//g; END {print "$x\n"}'< mis.txt > a10
real    0m0.051s ; user 0m0.048s
=========================14 heemayl
#!/usr/bin/env python2 // with open('mis.txt') as f: for line in f: print line.count('"')

$ time pyt > a11
real    0m0.052s ; user 0m0.052s
=========================15 enzotib
$ time  while IFS= read -r line; do   line="${line//[!a]/}"; echo "${#line}"; done < mis.txt  > a13
real    0m9.254s ; user 0m8.724s
=========================16 bleurp
$ time awk ' {print (split(q4312078q,a,"a")-1) }' mis.txt > a14
real    0m0.148s ; user 0m0.144s
Error several -1


#15 楼

这是一个简单的Python脚本,用于在文件的每一行中查找"的计数:

 #!/usr/bin/env python2
with open('file.txt') as f:
    for line in f:
        print line.count('"')
 


这里我们使用了内置的count类型的str方法。

#16 楼

grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1


grep可以完成所有繁重的工作:报告在每个行号找到的每个字符。剩下的只是对每行的计数求和,并格式化输出。

删除-n并获得整个文件的计数。在0.015秒内似乎很快。
并且可以处理字符(不是字节)。

#17 楼

a替换为要计算的字符。输出是每行的计数器。

perl -nE 'say y!a!!'


#18 楼

bash的解决方案。没有外部程序被调用(对于短字符串更快)。

如果值在变量中:

$ a='"Hello!"'


这将打印多少"它包含:

$ b="${a//[^\"]}"; echo "${#b}"
2