如果第 1 列出现至少 n 次,我怎样才能从文件中提取行?

如果第 1 列出现至少 n 次,我怎样才能从文件中提取行?

有没有办法显示包含特定字符串的行(如下例中的 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借助过程替代(<()),sortuniq

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)

使用

  1. 将脚本复制到一个空文件中,另存为get_lines.py
  2. 在脚本的头部,设置最小行数,以字符串开始。
  3. 使用输入和输出文件作为参数来运行它:

    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

相关内容