例如,要计算以下文本的每行中的
"
"hello!"
Thank you!
第一行有两行,第二行有0。
#1 楼
您可以使用sed
和awk
做到这一点:$ 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 楼
我只需要使用awkawk -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 楼
bash
,zsh
,yash
和ksh
的某些实现/版本中的另一种不依赖外部程序的实现:>
使用
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 楼
对于字符串,最简单的方法是tr
和wc
(无需使用awk
或sed
来消除过多的麻烦)-但请注意有关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 楼
我们可以将grep
与regex
结合使用,以使其更加简单和强大。 要计算特定字符。
$ 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
评论
只需补充一点,您可以为此编写自己的10行C程序,而不是使用带有sed的正则表达式来提高性能。您应该考虑根据输入文件的大小进行操作。