【问题标题】:String with optional double quotes converted to a list of lists带有可选双引号的字符串转换为列表列表
【发布时间】:2021-10-06 23:18:46
【问题描述】:

我确实有一些可能的字符串格式,我正在尝试将其转换为列表,但是当字符串中有双引号时我遇到了一些麻烦,这应该被转换为列表。
对于简单的格式:'test 3,9,tag1,G,comment 1'
可以很容易地用命令转换:values = line.split(',') 并得到 values = ['test 3','9','tag1','G','comment 1'] 的结果。

当字符串的格式看起来像这样时,问题就开始了:'test 4,77,tag2,Y,"comment 2, comment 3"',它应该被转换成一个列表,比如:['test 4','77','tag2','Y','"comment 2, comment 3"']

或格式:'test 6,1,"tag2, tag3, tag4",Y,"comment 2, comment 3"' 应转换为['test 6','1','"tag2, tag3, tag4"','Y','"comment 2, comment 3"']

显然,如果我对上述字符串使用line.split(',') 命令,我将得到列表['test 6','1','"tag2', 'tag3', 'tag4"','Y','"comment 2', 'comment 3"'],这不是正确的。

我的下一步是将双引号内的信息转换为一个列表,例如:['tag2', 'tag3', 'tag4'],但是一旦我设法将它们转换为单独的字符串,这将非常简单。

我考虑过使用正则表达式解决方案,但我似乎没有做对。 任何帮助将不胜感激。

【问题讨论】:

  • 让我猜猜,这些字符串来自.csv 文件?解析后真的要保留双引号吗?通常双引号表示“忽略引号内的逗号”,但逻辑值确实包含引号,因此您实际上最终会得到['test 6','1','tag2, tag3, tag4','Y','comment 2, comment 3']。您真的要保留双引号吗?
  • 如果您正在阅读 .csv,首先使用标准 Python 库中的 csv.reader 将节省您编写完全相同的东西的大量工作。根据您从中获得的结果,拆分仍需要拆分的字段将很简单

标签: python python-3.x string split


【解决方案1】:

不要重新发明轮子。现有的csv 模块可以很好地处理大量用于解析 CSV 的边缘条件。使用csv.reader() 功能正确读取列,然后您可以对需要它的列进行后处理:

输入.csv

test 3,9,tag1,G,comment 1
test 4,77,tag2,Y,"comment 2, comment 3"
test 6,1,"tag2, tag3, tag4",Y,"comment 2, comment 3"

test.py

import csv

with open('input.csv','r',newline='') as f:
    r = csv.reader(f)
    for line in r:
        line[1] = int(line[1])        # sample post-read processing
        line[2] = line[2].split(', ')
        line[4] = line[4].split(', ')
        print(line)

输出:

['test 3', 9, ['tag1'], 'G', ['comment 1']]
['test 4', 77, ['tag2'], 'Y', ['comment 2', 'comment 3']]
['test 6', 1, ['tag2', 'tag3', 'tag4'], 'Y', ['comment 2', 'comment 3']]

根据评论编辑

csv.reader docs:

csvfile 可以是任何支持iterator 协议并在每次调用其__next__() 方法时返回一个字符串的对象——file objects 和列表对象都适用。

从评论看来,您有一个字节流。只需将其包装在 TextIOWrapper 中,该TextIOWrapper 对流进行解码,因此它返回字符串而不是字节:

import csv
import io

# This is a file-like object returning a byte stream,
# like OPs comment.
csv_file = open('input.csv','rb')

with io.TextIOWrapper(csv_file,encoding='utf8') as f:
    r = csv.reader(f)
    for line in r:
        line[1] = int(line[1])        # sample post-read processing
        line[2] = line[2].split(', ')
        line[4] = line[4].split(', ')
        print(line)

如果您已经将数据保存在字符串中,请将其包装在 StringIO 中:

import csv
import io

data = '''\
test 3,9,tag1,G,comment 1
test 4,77,tag2,Y,"comment 2, comment 3"
test 6,1,"tag2, tag3, tag4",Y,"comment 2, comment 3"
'''

with io.StringIO(data) as f:
    r = csv.reader(f)
    for line in r:
        line[1] = int(line[1])        # sample post-read processing
        line[2] = line[2].split(', ')
        line[4] = line[4].split(', ')
        print(line)

csv.reader 还支持遍历字符串列表,因此也可以:

import csv

data = '''\
test 3,9,tag1,G,comment 1
test 4,77,tag2,Y,"comment 2, comment 3"
test 6,1,"tag2, tag3, tag4",Y,"comment 2, comment 3"
'''

r = csv.reader(data.splitlines())
for line in r:
    line[1] = int(line[1])        # sample post-read processing
    line[2] = line[2].split(', ')
    line[4] = line[4].split(', ')
    print(line)

【讨论】:

  • 这是一个很好的解决方案,但问题是我本地没有 csv 文件。它应该来自我的 Django 应用程序的文件上传。对象的类型是django.core.files.uploadedfile.InMemoryUploadedFile,我设法读取它的唯一方法是使用命令file_data = csv_file.read().decode("utf-8"),它将文件的内容返回到字符串
  • @FloraBilesiou 该信息应添加到您的问题中。 csv.reader 仍然是正确的解决方案。它接受“类文件对象”。基本上,任何带有.read() 方法的东西。它还需要 Unicode 字符串,但您可以将 csv_file 字节流包装起来进行解码。我会用一个例子来更新我的答案。
【解决方案2】:

如果我正确理解了您的问题。这可能不是最好的解决方案,但确实可以。

def my_splitter(input_string):
    temp_list = input_string.split('"')
    temp_list = [item.split(',') for item in temp_list]
    output_list = []
    for item in temp_list:
        output_list += [s for s in item if s]
    return output_list

input_string = 'test 6,1,"tag2, tag3, tag4",Y,"comment 2, comment 3"'
output_list =  my_splitter(input_string)
>>> output_list
['test 6', '1', 'tag2', 'tag3', 'tag4', 'Y', 'comment 2', 'comment 3']

在函数中,首先用 (") 分割字符串。得到以下列表:

['test 6,1,', 'tag2, tag3, tag4', ',Y,', 'comment 2, comment 3', '']

接下来,用 (,) 分割该列表中的每个元素,得到以下列表:

[['test 6', '1', ''], ['tag2', ' tag3', ' tag4'], ['', 'Y', ''], ['comment 2', ' comment 3'], ['']]

最后,您循环遍历每个子列表并去除开头和结尾的空白元素,并且仅在它们不只是空白的情况下附加项目。这是最终输出:

['test 6', '1', 'tag2', 'tag3', 'tag4', 'Y', 'comment 2', 'comment 3']

【讨论】:

  • 这可能适用于几个示例行,但csv.reader() 处理包含逗号双引号和新行的列值。这是工作的正确解决方案。
  • @MarkTolonen 感谢您提供的信息。
猜你喜欢
  • 2020-11-18
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-27
  • 1970-01-01
  • 2012-10-25
相关资源
最近更新 更多