我已经习惯了grep | uniq -c | sort -rn
,但这个案子有点复杂。如果您复制 Whatsapp 网络聊天,您会得到类似的内容
[3:14 pm, 25/09/2020] James Smith: Hello!
[6:42 pm, 25/09/2020] John Doe: hi
[6:43 pm, 25/09/2020] James Smith: I was wondering..
if blah blah
and also blah
[6:45 pm, 25/09/2020] James Smith: blah blah blah blah...
我正在尝试获取有关对话中每个人的一些统计信息,从字符数 ( wc -c
) 开始。
74 James Smith
2 John Doe
那怎么办呢?即,聊天中每个参与者的文本计数。在上面的示例中,Smith 贡献了 74 个字符,Doe 贡献了 2 个字符。
为了进行测试,我复制了 WhatsApp 网络聊天中符合假设的几行内容。要将它们轻松粘贴到文件中:xsel -b > filet_to_test.txt
.
有效假设:
- 消息可以有换行符。
- 无法包含
[6:45 pm, 25/09/2020]
第二行以上的字符串。 - 新消息/记录可能会以某种形式
[6:45 pm, 25/09/2020]
开始^\[\d{1,2}:\d{2}\s[ap]m,\s\d{2}/\d{2}/\d{4}\]\s
。 - 时间戳之后的用户名不包含冒号。
更理想的解决方案是可扩展的(也许使用磨坊主),例如获取范围内或一周中每一天/小时的字符/字数。
答案1
与@dani-garcia 类似的方法。首先将您的文件文明化为tab
单独的文件:
cat file |
tr "\n" "\000" |
sed "s/\x0\[/\n/g" |
sed "1 s/\[//; s/\]/\t/1; s/:/\t/2; s/\x0/ /g" |
sed -E "s/(^[^,]+), ([0-9]{1,2}).([0-9]{2}).([0-9]{4})/\4-\3-\2 \1/"
2020-09-25 3:14 pm James Smith Hello!
2020-09-25 6:42 pm John Doe hi
2020-09-25 6:43 pm James Smith I was wondering.. if blah blah and also blah
2020-09-25 6:45 pm James Smith blah blah blah blah...
通过tr
将所有\n
ewline 翻译为null
cat file | tr "\n" "\000" |
然后在有模式的地方sed
g
全局重新插入ewlines\n
null[
sed "s/\x0\[/\n/g" |
[
最后通过丢失行首来整理各个行1
sed "1 s/\[//;
将第一个替换]
为\t
ab
s/\]/\t/1;
将第二个替换:
为\t
ab
s/:/\t/2;
最后将剩余的替换null
为以避免将最初由
\n
ewline分隔的单词连接在一起
s/\x0/ /g"
并整理你的日期,以便它们排序得很好
sed -E "s/(^[^,]+), ([0-9]{1,2}).([0-9]{2}).([0-9]{4})/\4-\3-\2 \1/"
现在您已将字段分开,您可以随意排序、分组、计数或进行任何操作。
| awk -F'\t' '{chats[$2]++; words[$2]+=split($3,tmp," "); chars[$2]+=length($3)}
END{for (who in chats){
S=(chats[who]==1)?"":"s";
s=(words[who]==1)?"":"s";
print who" sent "chats[who]" message"S" with "words[who]" word"s" and "chars[who]" characters"}}'
James Smith sent 3 messages with 14 words and 73 characters
John Doe sent 1 message with 1 word and 2 characters
答案2
您可以将每条消息分成三个部分(日期、人员、消息),然后使用按要满足的条件索引的 awk 数组,最后打印数组中的所有值,例如:
awk '{printf "%s%s", (NR>1&&/^\[.*\]/?"\n":""),$0}END{print " "}' test.txt | sed 's/\(^\[.*\]\) \(.*\): \(.*\)/\1\t\2\t\3/g' | awk 'BEGIN{FS="\t"} {arr[$2] =arr[$2]$3} END{for (i in arr) print length(arr[i]),i}'
是test.txt
你的输入文件。
解释:
如果该行不以 开头,第一个命令 ( awk '{printf "%s%s", (NR>1&&/^\[.*\]/?"\n":""),$0}END{print " "}' test.txt
) 会删除换行符[blahblahblah]
,即它不是新消息,因此整个消息将在同一行中。
第二个命令 ( sed 's/\(^\[.*\]\) \(.*\): \(.*\)/\1\t\2\t\3/g'
) 将每一行分为三部分:日期(带有模式[.*]
)、人员(在日期和冒号之间)和消息。然后它输出每一行,每个部分用制表符分隔。
最后,第三个命令 ( awk 'BEGIN{FS="\t"} {arr[$2] =arr[$2]$3} END{for (i in arr) print length(arr[i]),i}'
) 使用按人员索引的 awk 数组,并输出每个人员的消息串联的长度。
假设
- 日期由
[
和分隔,]
并且中间不包含这些符号。 - 人名不包含冒号
- 消息可以包含字符串,就像
[6:45 pm, 25/09/2020]
它们不在新行的开头一样。
我不熟悉磨坊主,但您可能可以执行类似于可扩展所需解决方案的操作,更改最后一个 awk 命令。
也许这不是最有效的方法,但它确实有效。