2016-02-25 76 views
1

我用这个基于第二列“ParentID”的python程序分裂出一个非常大的csv。由于大量文件和每个进程限制,我最近更新为“a”而不是“w”。这样做我的标题是每次写入,而不是每个文件中的第一次。Python不写头文件

我更新,增加了“write_header = true”和“write_header =假”,但现在它只是写在第一个文件头......我有29,000文件

#!/usr/bin/env python3 
import binascii 
import csv 
import os.path 
import sys 
from tkinter.filedialog import askopenfilename, askdirectory 
from tkinter.simpledialog import askinteger 

def split_csv_file(f, dst_dir, keyfunc): 
    csv_reader = csv.reader(f) 
    header = next(csv_reader) 
    write_header = True 
    csv_writers = {} 
    for row in csv_reader: 
     k = keyfunc(row) 
     with open(os.path.join(dst_dir, k), mode='a', newline='') as output: 
      writer = csv.writer(output) 
      while write_header: 
       writer.writerow(header) 
       write_header = False 
      csv_writers[k] = writer 
      csv_writers[k].writerow(row[0:1]) 

def get_args_from_cli(): 
    input_filename = sys.argv[1] 
    column = int(sys.argv[2]) 
    dst_dir = sys.argv[3] 
    return (input_filename, column, dst_dir) 

def get_args_from_gui(): 
    input_filename = askopenfilename(
     filetypes=(('TXT','.txt'),('CSV', '.csv')), 
     title='Select CSV Input File') 
    column = askinteger('Choose Table Column', 'Table column') 
    dst_dir = askdirectory(title='Select Destination Directory') 
    return (input_filename, column, dst_dir) 

if __name__ == '__main__': 
    if len(sys.argv) == 1: 
     input_filename, column, dst_dir = get_args_from_gui() 
    elif len(sys.argv) == 4: 
     input_filename, column, dst_dir = get_args_from_cli() 
    else: 
     raise Exception("Invalid number of arguments") 
    with open(input_filename, mode='r', newline='') as f: 
     split_csv_file(f, dst_dir, lambda r: r[column-1]+'.txt') 
     # if the column has funky values resulting in invalid filenames 
     # replace the line from above with: 
     # split_csv_file(f, dst_dir, lambda r: binascii.b2a_hex(r[column-1].encode('utf-8')).decode('utf-8')+'.csv') 

下面是一个例子的文件被分割..

<option value=''>Choose SubGroup</option>, ParentID 
<option value='/1990-Accord-DX-Glass-s/37918.htm'>Glass</option>,Accord1990DX422F22A1BodyHardwareBackGlass 
<option value='/1990-Accord-DX-Glass-s/37919.htm'>Glass</option>,Accord1990DX422F22A1BodyHardwareBackGlass 
<option value='/1990-Accord-DX-Reveal-Moldings-s/69090.htm'>Reveal Moldings</option>,Accord1990DX422F22A1BodyHardwareBackGlass 
<option value='/1990-Accord-DX-Reveal-Moldings-s/69091.htm'>Reveal Moldings</option>,Accord1990DX422F22A1BodyHardwareBackGlass 
<option value='/1990-Accord-DX-Center-s/10331.htm'>Center</option>,Accord1990DX422F22A1BodyHardwareConsole 
<option value='/1990-Accord-DX-Cowl-s/16006.htm'>Cowl</option>,Accord1990DX422F22A1BodyHardwareCowl 
<option value='/1990-Accord-DX-Exterior-Trim-s/26889.htm'>Exterior Trim</option>,Accord1990DX422F22A1BodyHardwareFender 
<option value='/1990-Accord-DX-Exterior-Trim-s/26890.htm'>Exterior Trim</option>,Accord1990DX422F22A1BodyHardwareFender 

我怎样才能得到头每写输出文件只有一次?

回答

2

您在第一次编写标题时将write_header设置为false。因此,只有您打开的第一个文件才会获得该标题。

跟踪哪些文件头中的一组设置:

def split_csv_file(f, dst_dir, keyfunc): 
    csv_reader = csv.reader(f) 
    header = next(csv_reader) 
    header_written = set() 
    for row in csv_reader: 
     k = keyfunc(row) 
     with open(os.path.join(dst_dir, k), mode='a', newline='') as output: 
      writer = csv.writer(output) 
      if k not in header_written: 
       writer.writerow(header) 
       header_written.add(k) 
     writer.writerow(row[0:1]) 

你可能想调查使您的文件,通过追踪你最后一次写信给一个和关闭那些你没有写,打开更长最长。这需要一个自定义的类,当你通过键请求它们时,它会透明地跟踪文件,而不是放在答案中。

+0

这是有道理的。但是我是新来的蟒蛇,所以我的理解是有点不清楚,所以我只是想你的代码,并收到此错误:回溯(最近通话最后一个): 文件“”,第14行,在split_csv_file ValueError:关闭文件的I/O操作。 在处理上述异常,另一个异常: 回溯(最近通话最后一个): 文件 “”,9号线,在 文件 “” 17行,在split_csv_file AttributeError的:“_csv .writer'对象没有属性'close' >>> –

+0

啊,是的;我犯了两个错误:我遗留了'with'语句,忘记单独存储文件。稍后会纠正。 –

+0

我看到你的编辑。谢谢。但是这让我回想起来。我收到了同样的“太多打开的文件”错误,这就是为什么我切换到“打开”有没有一种方法来运行它并关闭文件并重新打开,如果该文本的另一个实例出现。它似乎正在做我所需要的。 –