我正在寻找一种方法来采取一个文本文件,有由制表符分隔的数据列:
file.txt
abcd abcd abcd
efgh efgh efgh
ijkl ijkl ijkl
mnop mnop mnop
qrst qrst qrst
使用awk,我想将每列数据保存为自己的文本文件,使用数字作为文件名。
但问题是没有办法预测它们将包含的文本的列数
我唯一知道的是,列之间将用制表符分隔。
,
awk '{ print $1 }' file
将打印第一列
:
awk '{ print $2 }' file
将打印第二列
然而,我希望保存每列作为自己的文件。
列数可以是100位以内的任意数
所有输出文件同时打开
一个GNU awk
的想法:
awk '{for (i=1;i<=NF;i++) print $i > i".out"}' file
指出:
- 将为每个输出文件打开一个文件描述符,并保持打开状态
- 许多
awk
实现对一次可以打开的文件数量有限制;打开和关闭文件非常耗时,因此从性能角度考虑,您需要限制打开和关闭操作的次数 -
GNU awk
对一次可以打开多少文件有相当高的限制 - 如果你有
GNU awk
,你收到一个错误,说明太多的打开文件描述符,然后让我们知道,我们可以看看另一个想法(例如:运行一个单独的awk
为每一组N列;使用内存解决方案(假设整个文件都可以放入内存中) - 你提到列由
tab spaces
分隔;我不知道你是什么意思…列由多个制表符和空格分隔?列由制表符或空格分隔?)这个答案使用awk's
默认字段分隔符'空白'(多个空格/制表符视为单个分隔符);如果您的字段由制表符分隔,但在字段中包含空格,则将awk '{for ...
更改为awk -F't' '{for ...
内存;一次打开一个输出文件;香草awk
假设输入文件可以装入内存:
一个应该适用于所有awk
口味的想法:
awk '
{ for (i=1;i<=NF;i++)
cols[i]=cols[i] (FNR==1 ? "" : ORS) $i
}
END { for (i=1;i<=NF;i++) {
outfile= i ".out"
print cols[i] > outfile
close(outfile)
}
}
' file
内存;一次打开一个输出文件;GNU awk
另一个使用GNU awk
(用于多维数组支持)的内存解决方案:
awk '
{ for(i=1;i<=NF;i++)
cols[i][FNR] = $i
}
END { for (i=1;i<=NF;i++) {
outfile= i ".out"
for (j=1;j<=FNR;j++)
print cols[i][j] > outfile
close(outfile)
}
}
' file
所有这三个答案产生:
$ head ?.out
==> 1.out <==
abcd
efgh
ijkl
mnop
qrst
==> 2.out <==
abcd
efgh
ijkl
mnop
qrst
==> 3.out <==
abcd
efgh
ijkl
mnop
qrst
绩效考核
设置:
# create a file with 5000 lines and 500 columns; ~19.5 MBytes
awk '
BEGIN { for (i=1;i<=5000;i++) {
printf "%s", "col_1"
for (j=2;j<=500;j++)
printf "t%s", "col_" j
print ""
}
}
' > multi_column.txt
250万个打开/关闭操作
运行打开/关闭500个输出文件的两个答案中的任意一个,对于5000个输入行,(即5000 x 500 = 2.5 million
打开/关闭操作):
- 在2分钟后被杀死,处理了800行
- 外推:~12.5分钟处理5000行
- 时间将(显然)根据硬件而变化(例如,Ed Morton报告他的答案需要10分钟)在他的笔记本电脑上)
所有(500)个输出文件同时打开
运行第一个答案(如上):
- 10秒生成500个文件,每个文件5000行
- 即使我们必须限制自己,比如一次处理20列……我们可以对输入文件进行25次传递,并且仍然在<7分钟(可以通过并行运行一些
awk
会话来进一步减少时间)
内存;一次打开一个输出文件;香草awk
运行第二个答案(上面)
- 6秒生成500个文件,每个文件5000行
内存;一次打开一个输出文件;GNU awk
运行第三个答案(如上):
- 3秒生成500个文件,每个文件5000行
- 先前的内存回答较慢,因为需要时间"查找并追加"新字段到长度不断增加的数组条目的末尾(
cols[i]=cols[i] (FNR==1 ? "" : ORS) $i
)
无论您的输入有多少列,都可以移植到所有的awks:
awk -F't' '{
for (i=1; i<=NF; i++) {
out = $i ".out"
if ( !seen[out]++ ) {
printf "" > out
}
print $i >> out
close(out)
}
}' file
你可以这样做:
awk 'NR==FNR{max=NF>max ? NF : max; next}
{for(i=1; i<=max; i++) {
fn=sprintf("%s.col", i)
print $i >> fn
close(fn)
}
}' file file
如果你的列宽度是一致的,你可以做一次:
awk 'FNR==1{max=NF}
{for(i=1; i<=max; i++) {
fn=sprintf("%s.col", i)
print $i >> fn
close(fn)
}
}' file
使用示例创建以下文件:
$ head *.col
==> 1.col <==
abcd
efgh
ijkl
mnop
qrst
==> 2.col <==
abcd
efgh
ijkl
mnop
qrst
==> 3.col <==
abcd
efgh
ijkl
mnop
qrst
使用数组避免每行重复写入
awk '
{
for(i=1; i<=NF; i++){
# saving columns in multi-array
# i = column, NR = line
a[i][NR] = $(i)
}
}
END{
# iterating through array
for (col in a){
joined = ""
# joining lines per column
for (line in a[col]){
joined = joined a[col][line] "n"
}
gsub(/n$/,"",joined)
# write all joined lines per column at once to col-file
print joined > col".out"
}
}
' file.txt