在 python 2 或 python 3 中编写 csv 文件的便携方式

Posted

技术标签:

【中文标题】在 python 2 或 python 3 中编写 csv 文件的便携方式【英文标题】:portable way to write csv file in python 2 or python 3 【发布时间】:2016-12-12 23:50:56 【问题描述】:

在我的 Windows 机器上,我通常在 python 2 中这样做以编写一个 csv 文件:

import csv
f = open("out.csv","wb")
cr = csv.writer(f,delimiter=';')
cr.writerow(["a","b","c"])
f.close()

现在 python 3 禁止将文本文件编写为二进制文件,那段代码不再工作了。这行得通:

import csv
f = open("out.csv","w",newline='')
cr = csv.writer(f,delimiter=';')
cr.writerow(["a","b","c"])
f.close()

问题是:newline 参数对于 Python 2 是未知的。

当然,省略换行符会导致 csv 文件包含太多 \r 字符,因此不可接受。

我目前正在执行向后兼容的过程,以逐步从 python 2 迁移到 python 3.5 在我的所有模块中都有很多这样的语句。

我的解决方案是将代码嵌入到自定义模块中,并且自定义模块返回文件处理程序 + 写入器对象。 python 版本检查是在模块内部完成的,它允许任何使用我的模块的模块在没有太多黑客攻击的情况下工作任何 python 版本。

有没有更好的办法?

【问题讨论】:

我不禁想知道@Raymond Hettinger(Python 核心开发人员和 csv 模块的创建者)会提出什么建议…… 【参考方案1】:

在 Windows 上,我发现了一种兼容 python 2 和 3 的方法来更改 csv lineterminator 选项(默认为 "\r\n",当文件在 Windows 中以文本模式打开时,\r 太多了)

import csv

with open("out.csv","w") as f:
    cr = csv.writer(f,delimiter=";",lineterminator="\n")
    cr.writerow(["a","b","c"])
    cr.writerow(["d","e","f"])
    cr.writerow(["a","b","c"])
    cr.writerow(["d","e","f"])

无论是什么 python 版本,它都会创建一个没有臭名昭著的“空白行”的 csv 文件。

唯一的缺点是在 Linux 上,这种方法会产生\r-free 文件,这可能不是标准的(尽管文件在 excel 中仍然可以正常打开,没有空行并且仍然有几行 :))

问题在 3.6.2 上仍然存在(就像我前一段时间应该检查的那样检查自己)

另一种方法是使用字典作为参数:

write_args = "mode":"wb" if bytes is str else "mode":"w","newline":""

(比较bytesstr是区分python 2和python 3的众多方法之一,在python 3中类型不同,这与我们当前的问题BTW非常相关)。

现在我们可以通过 args 解包来传递这些参数:

with open("out.csv",**write_args) as f:
    cr = csv.writer(f,delimiter=";")

【讨论】:

很有趣,因为在输出文件中生成的行仍然由"\r\n" 终止(在 Windows 上,在 Python 2 和 3 中)。 当然,因为当您在 Windows 上以文本模式写入 \n 时,它会写入 \r\n。但是对于csv,默认行终止符是\r\n(这可能是一个问题),所以在Windows上写这个会\r\r\n(文件不检查是否已经有\r!)。是的,您在 ping Raymond Hettinger 方面做得很好,因为我确信 lineterminator 需要在 csv python 3 模块中进行更新。我的愿景:将其更改为\n仅适用于 Windows 平台将解决所有问题。 我刚刚在 Python 2 和 3 中尝试过(在 Windows 上),有没有lineterminator="\n",但从来没有产生过\r\r\n——所以我不确定我是否理解您提到的问题。 @martineau:你见证了什么?没有 lineterminator,在 python 2 和 3 中它应该产生损坏的文件(每个数据行后有 1 个空行)。 在所有情况下,每一行都以\r\n 结束。我还将第一个writerow 更改为cr.writerow(["a","b\nx","c"]),它也更改了嵌入的换行符(并在字符串周围加上引号:即a;"b\r\nx";c 已写入文件)。【参考方案2】:

对于读取和写入 csv 文件,我也没有找到更好的方法 - 但是我将封装到一个单独的函数中,如下所示。优点是逻辑都在一个地方,而不是在多次需要时重复。

import csv
import sys

def open_csv(filename, mode='r'):
    """Open a csv file in proper mode depending on Python verion."""
    return(open(filename, mode=mode+'b') if sys.version_info[0] == 2 else
           open(filename, mode=mode, newline=''))

with open_csv('out.csv', 'w') as f:
    writer = csv.writer(f, delimiter=';')
    writer.writerow([1, 2, 3])
    writer.writerow(['a', 'b', 'c'])

open_csv() 实用程序可以通过使用@Jean-François Fabre 在 2020 年 12 月 8 日更新他的答案中显示的技术来稍微简化,以检测正在使用的 Python 版本:

def open_csv(filename, mode='r'):
    """Open a csv file in proper mode depending on Python verion."""
    return(open(filename, mode=mode+'b') if bytes is str else
           open(filename, mode=mode, newline=''))

【讨论】:

很好,但我找到了更好的方法。我更新了自己的答案。 @Jean-FrançoisFabre:我喜欢您在 2020 年 12 月 8 日的更新检查版本的方式,并且可以在我的回答中使用类似的内容而不是 sys.version 来确定要做什么。 FWIW,我也认为在同一个更新中使用的 args 解包也非常聪明。 感谢您的积极反馈!随时编辑您的答案。我不介意。

以上是关于在 python 2 或 python 3 中编写 csv 文件的便携方式的主要内容,如果未能解决你的问题,请参考以下文章

用python编写程序?

用Python 3.1.3 写几个进制转换的程序

在 Python 中编写适用于 Windows 中的 Python 2.7+ 和 Python 3.3+ 的 .CSV 文件

python要求编写程序,计算得出1000以内中是3或7的倍数的所有自然数之和

在 Python 3 中编写此生成器的替代方法是啥? [关闭]

OpenCV入门(C++/Python)- 使用OpenCV读取和编写视频