【问题标题】:Python - CSV: Large file with rows of different lengthsPython - CSV:具有不同长度行的大文件
【发布时间】:2011-02-02 17:35:12
【问题描述】:

简而言之,我有一个 20,000,000 行的 csv 文件,它具有不同的行长。这是由于过时的数据记录器和专有格式。我们得到以下格式的 csv 文件的最终结果。我的目标是将此文件插入到 postgres 数据库中。我该怎么做:

  • 保留前 8 列和我的最后 2 列,以获得一致的 CSV 文件
  • 在 csv 文件 ether 的第一个或最后一个位置添加一个新列。

1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0 img_id.jpg, -50

【问题讨论】:

  • 您能具体说明“向文件中添加列”是什么意思吗?此列的数据来自哪里?
  • 这将帮助我们了解您遇到问题的步骤。你不知道如何将行分成列吗?您不知道如何跳过某些列吗?如果您只是不知道如何开始,请继续说明,有人也可以回答。
  • @Mark Byers,添加列可以在开头或结尾。 @Wesley,我坚持的部分是如何跳过某些列;但是,我认为 Ignacio 可能已经在下面回答了这个问题

标签: python parsing csv etl


【解决方案1】:

抱歉,您需要用这个编写一些代码。当你有一个像这样的大文件时,值得检查所有文件以确保它与你的期望一致。如果您将不满意的数据放入数据库中,您将永远无法将其全部取出。

记住关于 CSV 的奇怪之处:它是一堆类似标准的混搭,在引用、转义、空字符、unicode、空字段 (",,,") 方面有不同的规则, 多行输入和空行。 csv 模块具有“方言”和选项,您可能会发现 csv.Sniffer 类很有帮助。

我推荐你:

  • 运行“tail”命令查看最后几行。
  • 如果表现良好,通过 csv 阅读器运行整个文件以查看 它打破了。制作“每行字段”的快速直方图。
  • 考虑“有效”范围和字符类型,并严格检查它们是否为 你读。特别注意异常的 unicode 或字符之外的字符 可打印范围。
  • 认真考虑是否要将额外的奇数球值保留在“行的其余部分”文本字段中。
  • 将任何意外的行放入异常文件中。
  • 修复您的代码以处理异常文件中的新模式。冲洗。重复。
  • 最后,再次运行整个程序,实际将数据转储到数据库中。

在完全完成之前不接触数据库,您的开发时间会更快。此外,请注意 SQLite 在只读数据上的速度非常快,因此 PostGres 可能不是最佳解决方案。

您的最终代码可能如下所示,但如果不知道您的数据,我无法确定,尤其是它的“表现如何”:

while not eof
    out = []
    for chunk in range(1000):
       try:
          fields = csv.reader.next()
       except StopIteration:
          break
       except:
          print str(reader.line_num) + ", 'failed to parse'"
       try:
          assert len(fields) > 5 and len(fields < 12)
          assert int(fields[3]) > 0 and int(fields[3]) < 999999
          assert int(fields[4]) >= 1 and int(fields[4] <= 12) # date
          assert field[5] == field[5].strip()  # no extra whitespace
          assert not field[5].strip(printable_chars)  # no odd chars
          ...
       except AssertionError:
          print str(reader.line_num) + ", 'failed checks'"
       new_rec = [reader.line_num]  # new first item
       new_rec.extend(fields[:8])   # first eight
       new_rec.extend(fields[-2:])  # last two
       new_rec.append(",".join(field[8:-2])) # and the rest
       out.append(new_rec)
    if database:
       cursor.execute_many("INSERT INTO raw_table VALUES %d,...", out)

当然,您的里程数会因此代码而异。这是伪代码的初稿。预计为输入编写可靠的代码需要一天的大部分时间。

【讨论】:

    【解决方案2】:

    您可以将文件作为文本文件打开并一次读取一行。是否存在不“拆分字段”的引用或转义逗号?如果没有,你可以这样做

    with open('thebigfile.csv', 'r') as thecsv:
        for line in thecsv:
            fields = [f.strip() for f in thecsv.split(',')]
            consist = fields[:8] + fields[-2:] + ['onemore']
            ... use the `consist` list as warranted ...
    

    我怀疑在我有 + ['onemore'] 的地方,您可能想要“添加一列”,正如您所说,包含一些非常不同的内容,但我当然无法猜测它可能是什么。

    不要将插入的每一行单独发送到数据库 - 2000 万次插入将花费 很长 时间。相反,将“制造一致”列表分组,将它们附加到一个临时列表 - 每次该列表的长度达到 1000 时,使用 executemany 添加所有这些条目。

    编辑:澄清一下,我建议使用csv 来处理您知道不“正确”的文件csv 格式:直接处理它可以让您更直接地控制(尤其是当您发现每行逗号数量不同之外的其他违规行为时)。

    【讨论】:

    • 这个答案有很多很好的建议。我不知道为什么会有反对票。 +1ed
    • 嗯.. 似乎有带引号的逗号会导致错误的问题。
    • @Charles,当然,这就是为什么我说“如果不是,......”;-)。如果您需要更复杂的“语法”来解析“不完美的 csv”并提取字段,您可以根据需要逐步应用尽可能多的解析改进(csv 模块,同时适合处理 good csv文件,只是不能为传入数据中的缺陷提供尽可能多的解决方法)。
    • 是的。确实如此。如果这不是一次性导入,我可能会看看 SnapLogic。您确实提出了所有免责声明,我只是怀疑 20MLOD 不会有单引号字符串。
    【解决方案3】:

    我建议使用csv 模块。这是我在其他地方完成的一些基于 CSV 处理的代码

    from __future__ import with_statement
    import csv
    
    def process( reader, writer):
        for line in reader:
            data = row[:8] + row[-2:]
            writer.write( data )
    
    def main( infilename, outfilename ):
        with open( infilename, 'rU' ) as infile:
            reader = csv.reader( infile )
            with open( outfilename, 'w') as outfile:
                writer = csv.writer( outfile )
                process( reader, writer )
    
    if __name__ == '__main__':
        if len(sys.argv) != 3:
            print "syntax: python process.py filename outname"
            sys.exit(1)
        main( sys.argv[1], sys.argv[2] )
    

    【讨论】:

    • 出于好奇,你为什么用 with 而不是 for?
    • @dassouki:forprocess() 中。
    【解决方案4】:

    csv读取一行,然后:

    newrow = row[:8] + row[-2:]
    

    然后添加您的新字段并将其写出来(也可以使用csv)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 2020-12-24
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2013-06-22
      相关资源
      最近更新 更多