我有一个 33GB 的管道分隔平面文件。
我需要从文件中提取特定列,其中第一列和第二十列满足条件。
我使用下面的代码来处理输入文件。
awk -F"|" '('$1~/^BL|^FR|^GF|^GP|^MC|^MF|^MQ|^NC|^PF|^PM|^RE|^TF|^WF|^YT/&&$20=="TRUE"') {print $0}' <input file> | cut -d'|' -f1-3,6,10,11,13,19,20 >> <output file>
$1
和$20
是输入文件中的列位置
这段代码工作正常。然而,提取数据需要近 1.5 小时。有没有办法更快地处理文件?
答案1
尝试使用grep
.
(
export LC_ALL=C
grep -E '^(BL|FR|[GMTW]F|GP|M[CQ]|NC|PM|RE|YT)([^|]*\|){19}TRUE(\||$)' |
cut -d'|' -f1-3,6,10,11,13,19,20
)
正如 @don_crissti 所建议的,假设所有行至少包含 20 个字段,您也可以尝试先进行剪切,具体取决于每行上字段的数量和长度以及匹配的行的比例可能会提供更好的性能:
(
export LC_ALL=C
cut -d'|' -f1-3,6,10,11,13,19,20 |
grep -xE '(BL|FR|[GMTW]F|GP|M[CQ]|NC|PM|RE|YT).*\|TRUE'
)
答案2
试试马克?使用1.34或以上版本。在某些人的示例中,处理大文件的任务可能会加速 8 倍:
为了与您当前的性能进行绝对比较,该任务花了 1 分钟(使用 mawk)处理 1GB。使用 Java (JIT) 代码的尝试也没有更快。
此外,在添加 UTF-8 支持后,许多实用程序的性能似乎有所下降。 A谷歌搜索表明这至少会对 awk 的某些版本产生非常显着的影响:尝试使用环境变量运行LC_ALL=C
(例如LC_ALL=C awk ...
)。
答案3
你至少可以摆脱cut
:
awk -F '|' 'BEGIN { OFS=FS } $20 == "TRUE" && /^(BL|FR|GF|GP|MC|MF|MQ|NC|PF|PM|RE|TF|WF|YT)/ { print $1,$2,$3,$6,$10,$11,$13,$19,$20 }' indata >outdata
我不知道这是否运行得更快,但它避免了必须将每行至少分成两次字段。
您也可以尝试先删除正确的列(以减少awk
过滤工作):
cut -d '|' -f 'columnspec' indata | awk -F '|' 'BEGIN { OFS=FS } $20 == "TRUE" && /^(BL|FR|GF|GP|MC|MF|MQ|NC|PF|PM|RE|TF|WF|YT)/ { print }' >outdata
另一种方法是将文件分割成可管理的块,并行过滤这些块,然后连接结果。请参阅split
Unix 上的手册。如果生成数百个文件,您可能必须使用-a
带有 split 的标志,但我建议计算数据内文件中的行数,并将其拆分为大约 10 个左右的文件。
答案4
使用Python的每行方法
如果第一列等于定义的字符串,下面的脚本将返回行中的任意列集。所需的匹配项和要返回的列都是运行脚本的参数。一个例子:
python3 /path/to/script.py /path/to/file.txt monkey 3 12 > output.txt
如果第一列等于“monkey”,则返回 file.txt 中各行的第 0、2 和 11 列(第一列为 0)
定时
在一个 30,000,000 行的几 GB 文件中,脚本在我 10 多年的旧机器上只用了不到一分钟就完成了工作。由于脚本读取并处理每行,我们可以假设消耗的时间或多或少是线性的,并且脚本将比您的命令快得多地完成这项工作。
剧本
#!/usr/bin/env python3
import sys
s = sys.argv[2]; cols = [int(n) for n in sys.argv[3:]]
with open(sys.argv[1]) as src:
for l in src:
l = l.split("|"); match = l[0].strip()
if match == s:
print(match, " ".join(list(l[i].strip() for i in cols)))
如何使用
- 将脚本复制到一个空文件中,另存为
get_cols.py
使用以下参数运行它:
- 源文件
- 第一列(字符串)所需的匹配
- 要输出的列
例如:
python3 /path/to/get_cols.py Germany 2 12 > output.txt
就是这样