如何读取带有“?”的CSV行?

7 投票
4 回答
5112 浏览
提问于 2025-04-15 18:29

一行简单的CSV(逗号分隔值)数据可以用字符串的分割功能来处理。但是,有些行可能会包含",例如:

"good,morning", 100, 300, "1998,5,3"

所以直接用字符串分割的方法就不能解决这个问题。

我的解决办法是先用,来分割这一行,然后再把字符串中开头或结尾的"合并起来。

对于这个问题,最佳的做法是什么呢?

我想知道有没有Python或F#的代码示例可以参考。

编辑:我更关心的是具体的实现细节,而不是使用某个库。

4 个回答

2

通用的实现细节大概是这样的(未经测试)

def csvline2fields(line):
    fields = []
    quote = None
    while line.strip():
        line = line.strip()
        if line[0] in ("'", '"'):
            # Find the next quote:
            end = line.find(line[0])
            fields.append(line[1:end])
            # Find the beginning of the next field
            next = line.find(SEPARATOR)
            if next == -1:
                break
            line = line[next+1:]
            continue
        # find the next separator:
        next = line.find(SEPARATOR)
        fields.append(line[0:next])
        line = line[next+1:]
3

来自 Python的CSV模块

读取一个普通的CSV文件:

import csv
reader = csv.reader(open("some.csv", "rb"))
for row in reader:
    print row

读取一个格式不同的文件:

import csv
reader = csv.reader(open("passwd", "rb"), delimiter=':', quoting=csv.QUOTE_NONE)
for row in reader:
    print row

在LinuxJournal.com上有一些很好的使用示例

如果你对细节感兴趣,可以看看“在字符串不符合CSV格式时,按逗号分割字符串并尊重引号”,里面有一些与这个问题相关的很棒的正则表达式,或者直接阅读CSV模块的源代码。

11

在Python中,有一个叫做csv的模块,可以处理这个问题。

编辑: 这个任务属于“构建词法分析器”的范畴。通常做这种事情的方法是构建一个状态机(或者使用一个词法分析器库/框架来帮你完成)。

这个任务的状态机可能只需要两个状态:

  • 第一个状态是初始状态,在这个状态下,它会把每个字符(除了逗号和换行符)都当作字段的一部分(例外:开头和结尾的空格),逗号作为字段的分隔符,换行符作为记录的分隔符。当它遇到一个开引号时,就会进入下一个状态。
  • 第二个状态是读取带引号的字段状态,在这个状态下,所有字符(包括逗号和换行符)都被视为字段的一部分,除了引号。一个引号后面没有跟着另一个引号就意味着读取带引号的字段结束(回到初始状态),而一个引号后面跟着另一个引号则被视为一个单独的引号(转义引号)。

顺便说一下,你的拼接解决方案在处理"Field1","Field2""Field1"",""Field2"时会出问题。

撰写回答