2013-07-15 90 views
0

天儿真好大家,更新CSV文件(添加/删除行)与Python 3

我有一个树莓派系统跟踪的工具,通过各种用户被检查出来。我已经设置好了,以便在用户签入时以及签出时执行系统扫描。通过比较两次扫描,我可以确定工具是否已被采用/返回。但是,我也有一个Log.csv文件,用于跟踪当前检出哪些工具。当一个工具被检出(这里没有问题)时,我可以添加到这个日志中,但是在返回工具时我无法删除该行。

我已经搜索过这个解决方案,但没有找到具体的东西。据我所知,你不能从CSV文件中删除单行?我将不得不重写该文件,该特定行被省略了?

这里是我到目前为止,包括对Log.csv文件中同时添加和删除行:

with open('Log.csv', 'a+') as f: 
    reader = csv.reader(f) 
    if tools_taken not in reader: 
     csv.writer(open('Log.csv', 'a+')).writerow([tools_taken]) 

with open('Log.csv', 'a+') as f: 
    reader = csv.reader(f) 
    if tools_returned in reader: 
     ??? 

记住,上面的代码被简化为保持简洁。我在想'阅读器'中的'如果工具'太模糊了。我可能会将其更改为:

for row in reader: 
    for field in row: 
     if field == tools_taken: 
      ??? 

我在正确的轨道上吗?任何输入在这里将非常感谢!

回答

1

我不认为csv在这里是正确的结构。您希望能够查找给定的工具,找出其tools_taken是否为True,或者更改它的tools_taken,或者从文件中删除工具,或者向该文件添加工具,对吗?

这是一个数据库是什么,比如shelve

import contextlib 
import shelve 

tools = shelve.open('Log.db', 'c', writeback=True) 
with contextlib.closing(tools): 
    # Add a tool 
    tools['hammer'] = {'name': 'Hammer', 'owner': 'Joe', 'tools_taken': False} 
    # Check if a tool is taken 
    if tools['screwdriver']['tools_taken']: 
     print('The screwdriver is taken!') 
    # Change a tool's taken status: 
    tools['screwdriver']['tools_taken'] = True 
    # Remove a tool 
    del tools['jackhammer'] 

换句话说,你可以它就像一个dict(在这种情况下,充分的dict S),但它跨越的自动持久运行。

+0

谢谢你,这看起来比继续沿着CSV路线简单得多。我之前从未使用Python的数据库,所以可能需要一些玩弄,但这绝对是实现我想要的最合适的手段。谢谢! – jars121

+1

@ jars121:类似'sqlite3'的_real_数据库稍微复杂一点......但是一个简单的'dbm'或'shelve'数据库比使用'dict'稍微难一些。 (但是有一些注意事项,所以一定要阅读文档。) – abarnert

+0

我是Python的新手,所以我还没有使用过字典,但我会在那一个上写下你的话!搁置数据库看起来非常适合这个应用程序,所以感谢您的关注! – jars121

1

据我所知,您不能从CSV文件中删除单个行?我将不得不重写该文件,该特定行被省略了?

没错。事实上,对于一般的文件来说也是如此。为了从文件中删除东西,你必须将文件的整个剩余部分向上移动,然后截断留下的残余物。你通常不想这样做,所以csv模块不会帮助你做到这一点。


那么,你如何创建一个新的CSV文件?三种方式:

  1. 以读取模式打开,读取整个文件,关闭,打开写入模式,写出所有东西,关闭。
  2. 重命名为Log.csv.bak,在读取模式下打开,在写入模式下打开Log.csv,并从一个拷贝到另一个。
  3. 打开Log.csv在读模式下,以写模式打开一个临时文件,从一个拷贝到另一个,然后以原子方式将临时文件重命名为Log.csv

第三个通常是最好的,但不幸的是,它很难得到它的权利在跨平台的方式,甚至只适用于Windows。 (然而,如果你只关心Unix,这很容易)。因此,我将展示第二个:

os.rename('Log.csv', 'Log.csv.bak') 
with open('Log.csv.bak') as infile, open('Log.csv', 'w') as outfile: 
    reader = csv.reader(infile) 
    writer = csv.writer(outfile) 
    for row in reader: 
     if not supposed_to_be_removed(row): 
      writer.writerow(row) 

就是这样。


这类似于你会写代替突变算法的复制算法的一个简单的列表方式:

newlist = [row for row in oldlist if not supposed_to_be_removed(row)] 

当然你可以写在迭代器方面,而不是清单:

newlist = (row for row in oldlist if not supposed_to_be_removed(row)) 

而事实上,你可以在这里使用完全相同的迭代器:

os.rename('Log.csv', 'Log.csv.bak') 
with open('Log.csv.bak') as infile, open('Log.csv', 'w') as outfile: 
    reader = csv.reader(infile) 
    writer = csv.writer(outfile) 
    newrows = (row for row in reader if not_supposed_to_be_removed(row)) 
    writer.writerows(newrows) 

你甚至可以把它变成一个班轮,如果你想:

writer.writerows(row for row in reader if not supposed_to_be_removed(row)) 

最后,csv文件是否真的是正确的答案在这里它可能是值得考虑的。如果你正在进行大量的操作,不断重复读取和重复写入文件将是一件痛苦的事情,而且会很慢。也许你可以将它保存在内存中,只需读写,启动和关闭,但必须确保不会丢失错误数据。看到我的另一个答案另一个选择。

+0

谢谢你的出色,详细的回复。上面的选项3的确是我从我的研究中推测出来的。与以下回应一样,我不认为CSV在这里是正确的选择。我在泵的帮助下完成原型工作,而且我也有使用CSV的经验,尽管很少。我一定会从现在开始转向数据库! – jars121

+1

如果你在Unix上,并且无法自己弄清楚,我可以用'tempfile.NamedTemporaryFile'来演示如何做选项3。 (尽管我敢打赌,已经回答了SO,所以请先尝试搜索。) – abarnert

+0

我确实在Unix上运行Raspberry Pi的Raspbian Linux。长期目标是将这个系统与各种Windows应用程序同步,所以我认为从一开始就保持跨平台兼容性是一个好主意。 – jars121