有没有办法显示包含特定字符串的行(如下例中的 reverent:::N),这些字符串在特定文件中不会出现 N 次?
我知道我可以用 计算某个字符串的出现次数grep
,将其写入新文件,然后用 计算行数wc -l
。但是,我有一个巨大的文件,我需要过滤掉第 1 列中的字符串在文件第 1 列中未出现 5 次的行。
非常感谢您的建议。
编辑file_1.txt 中的行按第一列的字母顺序排序。该文件包含大约 1,000,000 行,当然,我也可以将其拆分成几个小文件。输出的顺序无关紧要。
文件_1.txt
realism:::N 1.33 depth:::N 3.11 341
realism:::N 1.33 problem:::N 2.68 335
realism:::N 1.33 now:::ADV 1.48 335
realism:::N 1.33 life:::N 2.69 334
renowned:::ADJ 1.41 be:::V 1.85 15760
renowned:::ADJ 1.41 internationally:::ADV 2.23 9134
renowned:::ADJ 1.41 world:::N 4.36 6736
renowned:::ADJ 1.41 most:::ADV 2.38 5482
reverent:::ADJ 1.5 use:::V 2.78 25
reverent:::ADJ 1.5 sacred:::ADJ 1.77 25
reverent:::ADJ 1.5 music:::N 4.31 25
reverent:::ADJ 1.5 devout:::ADJ 2.46 25
reverent:::ADJ 1.5 devotion:::N 2.36 25
输出文件.txt
reverent:::ADJ 1.5 use:::V 2.78 25
reverent:::ADJ 1.5 sacred:::ADJ 1.77 25
reverent:::ADJ 1.5 music:::N 4.31 25
reverent:::ADJ 1.5 devout:::ADJ 2.46 25
reverent:::ADJ 1.5 devotion:::N 2.36 25
答案1
awk
借助过程替代(<()
),sort
和uniq
:
awk 'NR==FNR{a[$0]; next} {for (i in a) if ($1==i) {print}}' \
<(awk '{print $1}' file.txt | sort| uniq -c | awk '$1 >= 5 {print $2}') file.txt
awk '{print $1}' file.txt | sort| uniq -c | awk '$1 >= 5 {print $2}'
获取内容出现次数大于或等于 5 次的第一个字段。命令替换<()
将 STDOUT 替换为文件描述符,该文件描述符作为第一个参数传递给主工作awk
进程,原始输入文件作为第二个参数NR==FNR{a[$0]; next}
a
使用第一个文件 () 中的元素NR==FNR
作为键创建数组{for (i in a) if ($1==i) {print}}'
打印以file.txt
数组中的键a
作为第一个字段的行
明显的警告是,这种方法需要读取文件两次,对于较大的文件来说,这可能不是最佳解决方案(按照通常的定义) 输入文件,预期速度较高和/或资源使用率较低。
例子:
% cat file.txt
realism:::N 1.33 depth:::N 3.11 341
realism:::N 1.33 problem:::N 2.68 335
realism:::N 1.33 now:::ADV 1.48 335
realism:::N 1.33 life:::N 2.69 334
renowned:::ADJ 1.41 be:::V 1.85 15760
renowned:::ADJ 1.41 internationally:::ADV 2.23 9134
renowned:::ADJ 1.41 world:::N 4.36 6736
renowned:::ADJ 1.41 most:::ADV 2.38 5482
reverent:::ADJ 1.5 use:::V 2.78 25
reverent:::ADJ 1.5 sacred:::ADJ 1.77 25
reverent:::ADJ 1.5 music:::N 4.31 25
reverent:::ADJ 1.5 devout:::ADJ 2.46 25
reverent:::ADJ 1.5 devotion:::N 2.36 25
% awk 'NR==FNR{a[$0]; next} {for (i in a) if ($1==i) {print}}' <(awk '{print $1}' file.txt | sort| uniq -c | awk '$1 >= 5 {print $2}') file.txt
reverent:::ADJ 1.5 use:::V 2.78 25
reverent:::ADJ 1.5 sacred:::ADJ 1.77 25
reverent:::ADJ 1.5 music:::N 4.31 25
reverent:::ADJ 1.5 devout:::ADJ 2.46 25
reverent:::ADJ 1.5 devotion:::N 2.36 25
答案2
假设这些行已排序(分组)
既然你提到了按第一列的字母顺序排序:
下面的脚本将读取这些行,并将它们保存在缓冲区中,直到该行 从第一列中与前一个字符串相同的字符串开始。
如果不,将缓冲区添加到输出文件(仅)如果字符串数量达到一定值,随后缓冲区被清除并且该过程重新开始,直到文件完成。
虽然我还没有对大型文件进行时间测试,但对于大型文件来说,这应该相当快
剧本
#!/usr/bin/env python3
import sys
#-- set the minimum number below
n = 5
# don't change anything below
f = sys.argv[1]; out = sys.argv[2]; mark1 = ""; lines = []
def write_out(lines):
if len(lines) >= n:
with open(out, "a+") as wrt:
for line in lines:
wrt.write(line)
with open(f) as read:
for l in read:
mark2 = l.split()[0]
if mark2 == mark1:
lines.append(l)
else:
write_out(lines)
lines = [l]
mark1 = mark2
# add the last set of lines
write_out(lines)
使用
- 将脚本复制到一个空文件中,另存为
get_lines.py
- 在脚本的头部,设置最小行数,以字符串开始。
使用输入和输出文件作为参数来运行它:
python3 /path/to/get_lines.py <input_file> <output_file>
output_file
如果我们将限制设置为 5,则输出(如预期) :
reverent:::ADJ 1.5 use:::V 2.78 25
reverent:::ADJ 1.5 sacred:::ADJ 1.77 25
reverent:::ADJ 1.5 music:::N 4.31 25
reverent:::ADJ 1.5 devout:::ADJ 2.46 25
reverent:::ADJ 1.5 devotion:::N 2.36 25