上传具有固定格式的csv文件

时间:2012-01-19 16:27:08

标签: python csv

我有一个.csv文件,我的用户必须下载,输入一些数据并上传到我的网站。

根据下面的代码段,是否有更好的方法可以确保数据成功上传?我还应该检查什么?使用方言会更好吗?

def import(resident_file):

    try:
        file = resident_file.file.path
        reader = csv.reader(open(file, 'rU'), delimiter=',', quotechar='"')
        headerline = reader.next()

        for row in reader:
            try:
                # do stuff

            except Exception, e:
                print e

    except Exception, e:
        print e

我遇到的问题的一个示例是,当用户打开文件,输入数据并保存时,分隔符会从,更改为;。如何覆盖文档可以保存的各种类型的分隔符,因为它在不同的程序中打开,例如windows中的excel,mac中的excel,mac中的open office,linux中的open office等等。

问题的另一个例子是,当用户尝试将数据复制并粘贴到提供的模板中时,一切都会崩溃。

更新 我现在正在使用Sniffer课程,如下面的一个答案所述,但它仍然不是万无一失的。

更新代码SNIPPET

def bulk_import_residents(condo, resident_file):

    """
    COL 1       COL 2       COL 3           COL 4           COL 5        
    first_name  last_name   contact_number  unit_number     block_number

    """

    file_path = resident_file.file.path
    csvfile = open(file_path, 'rb')
    dialect =  csv.Sniffer().sniff(csvfile.read(1024))
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    headerline = reader.next()

    for row in reader:
        try:
            data = ResidentImportData()
            data.condo = condo
            data.file = resident_file
            data.first_name = row[0]
            data.last_name = row[1] 
            data.contact_number = row[2]
            data.unit_number = row[3]
            data.block_number = row[4]
            data.save()
        except Exception, e:
            print '{0}'.format(e)
            raise Http404('Wrong template format')

9 个答案:

答案 0 :(得分:2)

CSV是非格式化的。 Sniffer类不是万无一失的,因为它实际上不可能 100%可靠地检测所有给定的方言。

我认为你必须在90%的时间内使用Sniffer,并捕获无效的输入文件,分析它们,并扩展Sniffer以捕获它们。

答案 1 :(得分:2)

我完全赞同nfirvine(CSV是一种非格式) - 好吧,不是那么苛刻。但它是一种最小格式。它很松散。如果您使用CSV,预计事情会经常中断,因为听起来您已经遇到过这种情况。

我也同意迈克拜纳姆 - 使用像XML这样的东西。

但我明白,即使有更好的方法,也常常采用务实的方式。也许你必须坚持你的格式过多的原因......所以:两条路线。

路线1:CSV

我现在已经(正在做)这条路线。我的用户每天更新数据(几千条记录)。鉴于更新的频率和记录数量,我真的希望我已经走了第二条路线:在处理大量数据或更新时,可靠的数据验证可以节省大量时间。

那就是说。当你遇到CSV时。我建议你做以下事情:

  • 为您的用户提供good/common definition of CSV,即RFC 4180。确保您的客户了解您希望其文件包含的内容:
    • 标题行。
    • 逗号分离
    • 引用任何包含逗号的数据。
  • 除了这个定义,给你的用户一个CSV的样本(听起来你做的很好,好!)。说明您无法处理不符合数据定义的CSV文件。
  • 确保文本文件类型在导入之前符合您的预期 - 请参阅convert to/from Unix/Windows
  • 在您的CSV解析器中,您需要采用fail fast methodology,并确保您有一种机制可以在CSV文件不符合您期望的标准时通知您的用户。给他们尽可能多的信息(提供例外细节......如果没有,至少对你而言)。
  • 您遇到一个客户文件时遇到的这个问题表明,您可能希望为您的客户指明一些方向,因为您知道的工作正常。 Excel应该工作,或Open Office。我建议使用电子表格应用程序b / c,它们可以很好地导出到CSV并处理报价等。

并不是说你不能支持一些奇怪的东西,但总的来说 - 你想避免它,并且你想避免意外地导入形成错误的数据。

路线2:XML

我建议你做以下事情:

  • 使用架构定义(XSD)定义用户必须导入的数据。我想保留w3c definitions。但是good tutorials可以帮助您编写自己的XSD定义。

  • 为您的用户提供要填写的示例XML文件,以及对编辑器的建议。有great commercial ones和合理的free ones

  • 您可以阅读用户的XML文件,并确保如果validates,那么它的好处就可以了。就此而言,您的用户可以在发送给您之前validate

答案 2 :(得分:1)

啊刚刚找到了嗅探器类。

csvfile = open("example.csv", "rb")
dialect = csv.Sniffer().sniff(csvfile.read(1024))
csvfile.seek(0)
reader = csv.reader(csvfile, dialect)
# ... process CSV file contents here ...

答案 3 :(得分:1)

查看csv.Sniffer,它可以帮助您猜出文件正在使用的csv方言。

一旦您从嗅探器中猜到,请尝试使用该方言解析文件。如果您可以依赖的数据属性(例如,一定数量的字段),则将它们应用于每个检索到的记录作为完整性检查。

您还可以进行两阶段上传过程。首先上传文件并嗅探方言。然后向用户显示解析后几行数据的样子,并为用户提供覆盖某些方言设置的选项,以防它出错。然后在确认后处理csv。 (Excel中的“导入”对话框使用此多阶段方法。)

答案 4 :(得分:0)

我建议使用这种方法检查所有出现n-1次的字符(n是你需要的列数)。它可以为您提供第一个可能的答案或检查所有文件。

from collections import Counter
def snif_sep(txt, nbcol, force_all=False):
  pseps = None
  for line in txt.split('\n'):
    if line:
      psep = [k for k,v in Counter(line).items() if v==nbcol-1]
      if pseps is None:
        pseps = set(psep)
      else:
        pseps.intersection_update(psep)
      if len(pseps)==1 and not force_all:
        return pseps.pop()
      if len(pseps)==0:
        return None
  if len(pseps)==1:
    return pseps.pop()

答案 5 :(得分:0)

您是否考虑过使用XML格式? Excel具有XML格式,可以更容易解析并在Excel中轻松打开。

您也可以使用自己的xml格式。

http://msdn.microsoft.com/en-us/library/aa140066(office.10).aspx

    <?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
    <Styles>
        <Style ss:ID="sBold">
            <Font ss:Bold="1"/>
        </Style>
        <Style ss:ID="sDate">
            <NumberFormat ss:Format="General Date"/>
        </Style>
    </Styles>
    <Worksheet ss:Name="2100Q is 2009-Nov-11_17_43_13 ">
        <Table>
            <Row>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Date &amp; Time</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Operator ID</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Reading Mode</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Sample ID</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Sample Number</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Result</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Unit</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Notice</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Cal.Curve</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Cal.Time</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Cal.Status</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 1 Nom. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 1 Act. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 2 Nom. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 2 Act. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 3 Nom. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 3 Act. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 4 Nom. Value</Data>
                </Cell>
                <Cell ss:StyleID="sBold">
                    <Data ss:Type="String">Std. 4 Act. Value</Data>
                </Cell>
            </Row>
            <Row>
                <Cell ss:StyleID="sDate">
                    <Data ss:Type="DateTime">2009-11-10T11:23:30</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">BARBARA</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Normal</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">ABC-abc-1234</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="Number">001</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="Number">1.01</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">FNU</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String"/>
                </Cell>
                <Cell>
                    <Data ss:Type="String">StablCal</Data>
                </Cell>
                <Cell ss:StyleID="sDate">
                    <Data ss:Type="DateTime">2009-11-10T10:22:06</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">OK</Data>
                </Cell>
            </Row>
            <Row>
                <Cell ss:StyleID="sDate">
                    <Data ss:Type="DateTime">2009-11-10T10:24:15</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String"/>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Cal.Verification</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String"/>
                </Cell>
                <Cell>
                    <Data ss:Type="String"/>
                </Cell>
                <Cell>
                    <Data ss:Type="Number">1.01</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">FNU</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Verify Cal: Passed</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">StablCal</Data>
                </Cell>
                <Cell ss:StyleID="sDate">
                    <Data ss:Type="DateTime">2009-11-10T10:22:06</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">OK</Data>
                </Cell>
            </Row>
        </Table>
    </Worksheet>
</Workbook>

答案 6 :(得分:0)

您是否考虑过TAB分隔文件?您提到的所有软件都可以轻松读取和写入,并且比CSV给我的麻烦少得多。

尽管如此,我提供了一个热情的+1,让用户在一个已知的在线编辑器中编辑文件。 Google Docs,Zoho等提供共享文件和导出数据的功能 - 这使您可以负责格式并使其更容易解析。

如果您使用TSV,请确保清除数据以查找引号之间的引用字符串?你总是可以使用.strip ...

答案 7 :(得分:0)

老实说,如果他们有不同格式的文件,最简单的解决方案是给他们一个下拉菜单,让他们选择他们正在使用的程序。然后为该计划量身定制流程。

没有办法让一个进程能够覆盖任何和所有可能的格式化选项。但是通过这种方式将其分解,您可以根据需要添加它们,并为自己简化维护。

答案 8 :(得分:0)

允许您查看或导入CSV文件的工具必须面对这个常见问题。 工具包括数据库导入工具,Excel,开放式办公等。 我知道SOFA是在python中创建的,允许csv导入。

所有这些工具都有预览数据,以便用户可以确保它看起来不错。 至少如果预览看起来不对,他们可以选择他们想要纠正的csv分隔符。 他们用来创建csv文件的工具应该始终保持一致,所以如果它在预览中看起来没问题,那么对于文件的其余部分来说可能没问题。 APART来自棘手的罕见情况,其中数据被转义或用引号括起来。

如果文件不是太大,请尝试创建一组文件中出现的所有字符(不是a-z或0-9的字符)。现在确保在预览中包含每个出现的字符的行。然后,如果预览的这一部分搞砸了,用户可以更改引号。这是一个很好的预测器开销。您需要确保预览器按顺序显示行,以表示您故意错过的行。

如果无法预览,那么上帝可能会和你在一起。