当记录结束标记不是新行时,我可以使用 csv 模块吗?

can I use the csv module when the end of record marker is not a new line?

我想解析一个使用非 ascii 定界符的类 csv 文件。 csv 模块让我设置引号字符和字段分隔符。是否可以设置记录结束分隔符以便它可以与 csv 模块一起使用?

取一个类似 csv 的文件,而不是:

'"', ',', '\n'

它使用

'¦', '¶', '§'

例如

data = [
    [1,r'''text "could" be
'tricky'\'''],
    [2,r'or easy']
]

将表示为

'1¶¦text "could" be\n\'tricky\'\\¦§2¶¦or easy¦'

我知道如何使用 split 等解决这个问题。但是 csv 模块有更好的方法吗?

此表达式生成示例:

chr(167).join(
[
        chr(182).join(
            [
                '\xa6{}\xa6'.format(val) if type(val)==str else str(val)
                for val in row
            ]
        ) for row in data
    ])

您无法使用 csv 模块读取此类文件。有一个名为 lineterminator 的选项,但 documentation 表示:

The reader is hard-coded to recognise either '\r' or '\n' as end-of-line, and ignores lineterminator. This behavior may change in the future.

您显然可以使用此 lineterminator 参数来 写入 这样的文件,但您无法使用 csv模块。

不,您不能为此直接使用 csv.reader(),因为 Dialect.lineterminator parameter 是硬编码的:

Note: The reader is hard-coded to recognise either '\r' or '\n' as end-of-line, and ignores lineterminator. This behavior may change in the future.

您必须围绕 reader 创建一个包装器来翻译您的行终止符:

class LineTerminatorTranslator(object):
    def __init__(self, orig, terminator, buffer=2048):
        self._orig = orig
        self._terminator = terminator
        self._buffer = buffer

    def __iter__(self):
        terminator = self._terminator
        buffer = ''

        if hasattr(self._orig, 'read'):
            # read in chunks, rather than in lines, where possible
            iterator = iter(lambda: self._orig.read(self._buffer), '')
        else:
            iterator = iter(self._orig)

        while True:
            try:
                while terminator not in buffer:
                    buffer += next(iterator)
            except StopIteration:
                # done, yield remainder
                yield buffer
                return
            entries, _, buffer = buffer.rpartition(terminator)
            for entry in entries.split(terminator):
                yield entry

这会以 2kb 的块(可配置)读取输入文件,并按给定的行终止符拆分行。

因为 csv.reader() 可以处理任何可迭代对象,代码也可以接受其他可迭代对象,但如果此类可迭代对象每次迭代都产生大字符串,则效率会降低。

代码应该适用于 Python 2 和 3。

演示:

>>> import csv
>>> import io
>>> sample = '1¶¦text "could" be\'tricky\n\'\\¦§2¶¦or easy¦'
>>> input = LineTerminatorTranslator(io.StringIO(sample), '§')
>>> list(csv.reader(input, delimiter='¶', quotechar='¦'))
[['1', 'text "could" be\'tricky\n\'\\'], ['2', 'or easy']]

略做作Python2版本:

>>> import csv
>>> from cStringIO import StringIO
>>> sample = '1P|text "could" be\'tricky\n\'\\|T2P|or easy|'
>>> input = LineTerminatorTranslator(StringIO(sample), 'T')
>>> list(csv.reader(input, delimiter='P', quotechar='|'))
[['1', 'text "could" be\'tricky\n\'\\'], ['2', 'or easy']]