使用 csv reader 在文本文件中保留双引号

Keep double quotes in a text file using csv reader

您好,我有一个包含字符串的文本文件:

你好,"foo, bar"

我想将其拆分为一个列表:

['hello', '"foo, bar"']

有什么办法可以实现吗?

我现在正在尝试:

for line in sys.stdin: csv_file = StringIO.StringIO(line) csv_reader = csv.reader(csv_file)

我希望它们分成两个字符串,即:

'hello' and '"foo, bar"'

好吧,这花了很长时间才找到解决方案,而且一点也不漂亮,但是:

>>> import re
>>> s = 'hello,"foo, bar"'
>>> 
>>> replacements = {}
>>> m = re.search("\".*\"", s)
>>> while m:
...     key = 'unique_phrase_' + str(len(replacements))
...     replacements[key] = s[m.span()[0]:m.span()[1]]
...     s = re.sub("\".*\"", key, s, count=1)
...     m = re.search("\".*\"", s)
... 
>>> list_from_string = s.split(",")
>>> final_list = []
>>> for element in list_from_string:
...     for key in replacements.keys():
...             if re.match(key, element):
...                     final_list.append(re.sub(key, replacements[key],   element))
...             else:
...                     final_list.append(element)
... 
>>> 
>>> print final_list
['hello', '"foo, bar"']

在我看来很难看,但找不到任何明确的方法让它更像 pythonic。

晚会有点晚了,但在 CSV 库中有 quoting 应该可以满足您的要求(设置为 QUOTE_NONE)

假设您从 CSV 中读取了一行:

from StringIO import StringIO
import csv

infile = StringIO('hello,"foo, bar"')
reader = csv.reader(infile)
row = reader.next()  # row is ['hello', 'foo, bar']

行中的第二个值是 foo, bar 而不是 "foo, bar"。这不是什么 Python 怪事,而是对 CSV 语法的合理解释。引号可能不是为了作为值的一部分而放在那里,而是为了表明 foo, bar 是一个值,不应根据逗号拆分为 foobar (,)。另一种解决方案是在创建 CSV 文件时转义逗号,因此该行看起来像:

hello,foo \,bar

所以想要保留这些引语是一个很奇怪的请求。如果我们更多地了解您的用例和更大的图景,我们可以更好地帮助您。你想达到什么目的?输入文件从哪里来?它真的是 CSV 还是其他一些看起来相似的语法?例如,如果您知道每一行都包含两个用逗号分隔的值,并且第一个值从不包含逗号,那么您可以只在第一个逗号处拆分:

print 'hello,"foo, bar"'.split(',', 1)  # => ['hello', '"foo, bar"']

但我怀疑输入有这样的限制,这就是为什么需要引号之类的东西来解决歧义。

如果您尝试再次写入 CSV,那么在您这样做时将重新创建引号。他们不必在中间列表中:

outfile = StringIO()
writer = csv.writer(outfile)
writer.writerow(row)
print outfile.getvalue()

这将打印

hello,"foo, bar"

您可以通过设置新方言来自定义准确的 CSV 输出。

如果您想获取行中应用了适当引用规则的各个值,这是可能的,但这有点麻烦:

# We're going to write individual strings, so we don't want a line terminator
csv.register_dialect('no_line_terminator', lineterminator='')

def maybe_quote_string(s):
    out = StringIO()

    # writerow iterates over its argument, so don't give it a plain string
    # or it'll break it up into characters
    csv.writer(out, 'no_line_terminator').writerow([s])

    return out.getvalue()

print maybe_quote_string('foo, bar')
print map(maybe_quote_string, row)

输出为:

"foo, bar"
['hello', '"foo, bar"']

这是我最接近回答您问题的时间。它并不是真正地 保留 双引号,而是删除它们并使用可能与最初将它们放在那里的相同规则将它们添加回去。

我再说一遍,这个问题你可能走错路了。其他人可能会同意。这就是为什么你很难得到好的答案。您要解决的更大问题是什么?我们可以帮助您更好地实现这一目标。

有点取决于您的用例。如果只有 "s 存在于包含逗号的值(例如“foo,bar”),那么您可以使用 CSV 编写器将它们放回原处。

import io
import csv

infile = io.StringIO('hello,"foo, bar"')
outfile = io.StringIO()
reader = csv.reader(infile)
for row in reader:
    inList = row
    break
print(inList)
# As an output string
writer = csv.writer(outfile)
writer.writerow(inList)
outList = outfile.getvalue().strip()
print(outList)
# As a List
outList = []
for i in range(len(inList)):
    outfile = io.StringIO()
    writer = csv.writer(outfile)
    writer.writerow([inList[i]])
    outList.append(outfile.getvalue().strip())
print(outList)

输出

['hello', 'foo, bar']
hello,"foo, bar"
['hello', '"foo, bar"']

但是,如果您有其他想要保留的不必要的 "s(例如'"hello","foo,bar",humbug')并且所有包含 , 的字段都将正确地包含在 "s 中,那么您可以拆分 , 上的行并查找 'broken' 字段(以 " 开头但不要以 " 结尾)

line = '"hello","foo, bar",humbug'
fields = line.split(',')
print(fields)
values = []
i = 0
while i < len(fields):
    # If a field doesn't start with a ", or starts and ends with "s
    if (fields[i][0] != '"') or (fields[i][-1] == '"'):
        values.append(fields[i])        # It's a stand alone value
        i += 1
        continue
    value = fields[i]           # A value that has been split
    i += 1
    while i < len(fields):
        value += ',' + fields[i]
        i += 1
        if value[-1] == '"':     # The last part would have ended in a "
            break
    values.append(value)
print(values)

输出

['"hello"', '"foo', ' bar"', 'humbug']
['"hello"', '"foo, bar"', 'humbug']