我有40,000个数据文件。每个文件在单列中包含1445行浮点数。现在我需要以不同的顺序重新排列数据。
需要收集每个数据文件中的第一个数字并将其转储到新文件中(假设为abc1.dat)。此特定文件(abc1.dat)将包含40,000个数字。
每个数据文件中的第二个数字需要被提取并转储到另一个新文件中(比方说abc2.dat)。这个新文件也将包含40,000个数字。但只有每个数据文件中的第二个数字。
在这个操作结束时我应该有1445个文件(abc1.dat,abc2.dat,... abc40000.dat),每个文件包含40,000个数据。
如何实现这一目标? (使用Linux Ubuntu 11.10 - 64位)
感谢任何帮助。提前谢谢。
答案 0 :(得分:5)
40,000 * 1445并不是那么多,它应该适合记忆。所以,在Perl(未经测试)中:
#!/usr/bin/perl
use strict;
use warnings;
my @nums;
# Reading:
for my $file (0 .. 40_000) {
open my $IN, '<', "file-$file" or die $!;
while (<$IN>) {
chomp;
$nums[$file][$.-1] = $_;
}
}
# Writing:
for my $line (0 .. 1444) {
open my $OUT, '>', "abc$line.dat" or die $!;
for my $file (0 .. 40_000) {
print $OUT $nums[$file][$line], "\n";
}
}
答案 1 :(得分:3)
如果您可以一次打开所有1445个输出文件,这很容易:
paths = ['abc{}.dat'.format(i) for i in range(1445)]
files = [open(path, 'w') for path in paths]
for inpath in ('input{}.dat'.format(i) for i in range(40000)):
with infile as open(inpath, 'r') as infile:
for linenum, line in enumerate(infile):
files[linenum].write(line)
for f in files:
f.close()
如果你可以将所有内容都安装到内存中(听起来这应该是大约0.5-5.0 GB的数据,这对于具有8GB RAM的64位机器来说可能没问题......),你可以这样做:< / p>
data = [[] for _ in range(1445)]
for inpath in ('input{}.dat'.format(i) for i in range(40000)):
with infile as open(inpath, 'r') as infile:
for linenum, line in enumerate(infile):
data[linenum].append(line)
for i, contents in enumerate(data):
with open('abc{}.dat'.format(i), 'w') as outfile:
outfile.write(''.join(contents)
如果这些都不合适,你可能想要某种混合体。例如,如果您一次可以执行250个文件,请执行6个批处理,并跳过每个batchnum
中的infile
* 250行。
如果批处理解决方案太慢,则在每个文件的每个批处理结束时,隐藏infile.tell()
,当您再次返回该文件时,请使用infile.seek()
返回该处。像这样:
seekpoints = [0 for _ in range(40000)]
for batch in range(6):
start = batch * 250
stop = min(start + 250, 1445)
paths = ['abc{}.dat'.format(i) for i in range(start, stop)]
files = [open(path, 'w') for path in paths]
for infilenum, inpath in enumerate('input{}.dat'.format(i) for i in range(40000)):
with infile as open(inpath, 'r') as infile:
infile.seek(seekpoints[infilenum])
for linenum, line in enumerate(infile):
files[linenum].write(line)
seekpoints[infilenum] = infile.tell()
for f in files:
f.close()
答案 2 :(得分:2)
你应该可以逃脱这样的单线:
perl -nwe 'open my $fh, ">>", "abc${.}.dat" or die $!;
print $fh $_; close ARGV if eof;' input*.dat
它将打开一个新的输出文件,用于附加输入文件的每一行。输出文件将根据输入文件的当前行号命名。最后,我们需要显式关闭ARGV文件句柄以重置行号变量$.
。
您可以使用glob控制输入文件的顺序,或者如果您愿意,可以使用perl控制输入文件的顺序。我选择了泛型glob,因为你没有指定这些行应按特定顺序排列。
效率方面,我不认为为每一行打开一个新文件会非常耗时,因为perl在文件操作上相当快。
请注意,您不需要关闭输出文件句柄,因为它在超出范围时会自动关闭。另请注意,它不会关心您的文件大小。
答案 3 :(得分:2)
击:
cat file1 file2 ... file40000 | split -n r/1445 -d - outputprefix
假设所有文件都有1445行,写入outputprefix0000,outputprefix0001,... outputprefix1444。
有点慢,但它有效:)
答案 4 :(得分:1)
创建文件后,运行大约需要4分钟,笔记本电脑上使用3.6GB的RAM。如果你的机器有8GB的RAM,那应该没问题。
#!/usr/bin/env python2.7
import random
NUMFILES = 40000
NUMLINES = 1445
# create test files
for i in range(1, NUMFILES + 1):
with open('abc%s.dat' % i, 'w') as f:
for j in range(NUMLINES):
f.write('%f\n' % random.random())
data = []
# load all data into memory
for i in range(1, NUMFILES + 1):
print i
with open('abc%s.dat' % i) as f:
lines = f.readlines()
data.append(lines)
# write it back out
for j in range(len(data[0])):
with open('new_abc%s.dat' % (j + 1), 'w') as f:
for i in range(len(data)):
f.write(data[i][j])
我将所有内容都保留为字符串,以避免反序列化然后重新序列化浮点数时的精度错误。
您是否需要更快,更少资源的东西才能定期运行,或者这是一次性转换?
答案 5 :(得分:1)
为了完整起见,因为[fortran]标签,Fortran中的一个迟来的例子。它逐个打开文件并将所有数据存储在内存中。
program copy
implicit none
character(1024) :: filename
integer :: i, unit, infiles, outfiles
parameter (infiles = 40000, outfiles = 1445)
real :: data(infiles, outfiles)
do i = 1, infiles
write(filename, '("path/to/file", I0, ".dat")') i
open(newunit = unit, file = filename, action = 'read')
read(unit, *) data(i,:)
close(unit)
enddo
do i = 1, outfiles
write(filename, '("path/to/abc", I0, ".dat")') i
open(newunit = unit, file = filename, action = 'write')
write(unit, '(G0)') data(:,i)
close(unit)
enddo
end program
注意:它可能会很慢。
答案 6 :(得分:0)
在awk中,它非常简单:
awk '{print >> "abc" FNR ".dat}' files*
我不确定awk是否能够处理40,000个打开的文件句柄。
答案 7 :(得分:0)
以下适用于solaris。
nawk '{x="abc"FNR".txt";print $1>x}' file1 file2
你无论如何都可以:
nawk '{x="abc"FNR".txt";print $1>x}' file*
用于引用所有40k文件