我是python的新手。我的要求,如果我必须使用awk这么简单,如下所示,
下面提到的文件(test.txt)是制表符分隔的,
1 a b c
1 a d e
1 b d e
2 a b c
2 a d e
3 x y z
我想要的输出
文件1.txt应具有以下值
a b c
a d e
b d e
文件2.txt应具有以下值
a b c
a d e
文件3.txt应具有以下值
x y z
原始文件在第一列上排序。我不知道我必须拆分的行号。它必须是价值的变化。使用awk,我会像
一样编写它awk -F"\t" 'BEGIN {OFS="\t";} {print $2","$3","$4 > $1}' test.txt
(性能明智,python会更好吗?)
答案 0 :(得分:1)
Awk是完美的,应该快得多。速度真的是一个问题,你的输入有多大?
$ awk '{print $2,$3,$4 > ("file"$1)}' OFS='\t' file
<强>演示:强>
$ ls
file
$ cat file
1 a b c
1 a d e
1 b d e
2 a b c
2 a d e
3 x y z
$ awk '{print $2,$3,$4 > ("file"$1)}' OFS='\t' file
$ ls
file file1 file2 file3
$ cat file1
a b c
a d e
b d e
$ cat file2
a b c
a d e
$ cat file3
x y z
答案 1 :(得分:0)
这样的事情应该做你想做的事。
import itertools as it
with open('test.txt') as in_file:
splitted_lines = (line.split(None, 1) for line in in_file)
for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
with open(num + '.txt', 'w') as out_file:
out_file.writelines(line for _, line in group)
with
语句允许安全地使用资源。在这种情况下,它们会自动关闭文件。splitted_lines = (...)
行在占据每一行的字段上创建一个可迭代,并产生第一个元素对,其余的行。itertools.groupby
函数是执行大部分工作的函数。它遍历文件的行并根据第一个元素对它们进行分组。(line for _, line in group)
遍历“分割线”。它只删除第一个元素并只写入其余的行。 (_
只是一个标识符。我可以使用x
或first
,但我_
通常用于表示您拥有的内容分配,但你不使用)我们可以简化代码。例如,最外面的with
不太可能有用,因为我们只是在读取模式下打开文件,而不是修改它。
删除它我们可以取消缩进:
import itertools as it
splitted_lines = (line.split(None, 1) for line in open('test.txt'))
for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
with open(num + '.txt', 'w') as out_file:
out_file.writelines(line for _, line in group)
我做了一个非常简单的基准来测试python解决方案与awk解决方案。 性能大致相同,python稍微更快使用一个文件,其中每行有10个字段,100个“行组”,每个随机大小在2到30个元素之间。
python代码的时间:
In [22]: from random import randint
...:
...: with open('test.txt', 'w') as f:
...: for count in range(1, 101):
...: num_nums = randint(2, 30)
...: for time in range(num_nums):
...: numbers = (str(randint(-1000, 1000)) for _ in range(10))
...: f.write('{}\t{}\n'.format(count, '\t'.join(numbers)))
...:
In [23]: %%timeit
...: splitted_lines = (line.split(None, 1) for line in open('test.txt'))
...: for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
...: with open(num + '.txt', 'w') as out_file:
...: out_file.writelines(line for _, line in group)
...:
10 loops, best of 3: 11.3 ms per loop
Awk时间:
$time awk '{print $2,$3,$4 > ("test"$1)}' OFS='\t' test.txt
real 0m0.014s
user 0m0.004s
sys 0m0.008s
请注意0.014s
约为14 ms
。
无论如何,根据操作系统的负载,时间可能会有所不同,并且有效地同步很快。在实践中,几乎所有的时间都是从文件读取/写入文件,这是由python和awk高效完成的。我相信使用C你不会看到巨大的速度增益。
答案 2 :(得分:0)
我的版本:
for line in open('text.txt', 'r'):
line = line.split(' ')
doc_name = line[0]
content = ' '.join(line[1:])
f = open('file' + doc_name, 'a+')
f.write(content)
答案 3 :(得分:0)
如果你有一个非常大的文件,awk会打开并关闭每一行上的文件来做追加,不是吗?如果这是一个问题,那么C ++有速度和容器类来很好地处理任意数量的打开输出文件,这样每个文件只能打开和关闭一次。这是标记的Python,它几乎一样快,假设I / O时间占主导地位。
避免Python中额外开/关开销的版本:
# iosplit.py
def iosplit(ifile, ifname="", prefix=""):
ofiles = {}
try:
for iline in ifile:
tokens = [s.strip() for s in iline.split('\t')]
if tokens and tokens[0]:
ofname = prefix + str(tokens[0]) + ".txt"
if ofname in ofiles:
ofile = ofiles[ofname]
else:
ofile = open(ofname, "w+")
ofiles[ofname] = ofile
ofile.write( '\t'.join(tokens[1:]) + '\n')
finally:
for ofname in ofiles:
ofiles[ofname].close()
if __name__ == "__main__":
import sys
ifname = (sys.argv + ["test.txt"])[1]
prefix = (sys.argv + ["", ""])[2]
iosplit(open(ifname), ifname, prefix)
命令行用法是python iosplit.py
默认为空,将作为每个输出文件名的前缀。调用程序提供了一个文件(或类文件对象),因此您可以使用StringIO对象甚至列表/元组字符串来驱动它。
警告:这将删除行中制表符前面或后面的任何空格。内部空间不会被触及。所以当写入1.txt时,“1 \ ta b \ t c \ t d”将被转换为“a b \ tc \ td”。