在一个字符串中找到列表

4

我看到很多例子都是在字符串或列表中查找字符串,但是如何在一个字符串中查找一个列表呢?例如,我有一个包含数据列的csv文件,最后一列可以是一个字符串,有时候它也可以是一个列表。下面是一个子集,显示了最后3列的内容。

TRUE, 93877, S26476961
TRUE, 93878, ['S26489167', 'S26492524']
FALSE, 93879, S26476962
FALSE, 93880, ['S26489168', 'S26492527', 'S26492528']

一开始我试图按逗号解析csv文件的每一行,但它会解析列表内的逗号(创建额外的列)。我只想将列表识别为一个数据单元,以便我可以将其作为'n'项列表来处理。

@TemporalWolf的评论非常有帮助,因为如果我使用csv模块(特别是csv.reader)如下所示...

reader = csv.reader(inFile)
for row in reader:
    print(row)

它将列表保持在单列中。现在仍然存在问题,它只是一个字符串。换句话说,row[n][0]返回左括号([),但我想将其转换为列表。


2
使用csv库。 - TemporalWolf
另外,考虑使用pandas - jacoblaw
尝试在逗号上分割,但不要跟随或前置字符':[re.split("(?<!'), (?!')", line) for line in open(filename)] - Abdou
(a) 之后你想用这些数据做什么? (b) 你觉得把第一行改成TRUE,93877,['S26476961']怎么样?换句话说,所有行都以列表结尾。 - Bill Bell
@BillBell 如果最后一列的所有值都是列表(在某些情况下是单项列表),那么这将是可以的。 - jbchurchill
3个回答

4

这依赖于您的列表元素所引用的'字符。利用这个信息,它只在没有跟随或前置该字符的逗号处进行分割,使用正则表达式:

import re
import pandas as pd
import io


text = """TRUE, 93877, S26476961
TRUE, 93878, ['S26489167', 'S26492524']
FALSE, 93879, S26476962
FALSE, 93880, ['S26489168', 'S26492527', 'S26492528']"""

with io.StringIO(text) as f:
    for line in f:
        print(re.split("(?<!'), (?!')", line.strip()))


# ['TRUE', '93877', 'S26476961']
# ['TRUE', '93878', "['S26489167', 'S26492524']"]
# ['FALSE', '93879', 'S26476962']
# ['FALSE', '93880', "['S26489168', 'S26492527', 'S26492528']"]

# Or with pandas

with io.StringIO(text) as f:
    print(pd.read_csv(f,
                  header=None,
                  sep="(?<!'), (?!')",
                  engine='python'))

#        0      1                                        2
# 0   True  93877                                S26476961
# 1   True  93878               ['S26489167', 'S26492524']
# 2  False  93879                                S26476962
# 3  False  93880  ['S26489168', 'S26492527', 'S26492528']

编辑:

如果你正在使用Python 2,你需要将文本转换为Unicode格式(在文本前加上字符u)才能使用io.StringIO:

import re
import pandas as pd
import io


text = u"""TRUE, 93877, S26476961
TRUE, 93878, ['S26489167', 'S26492524']
FALSE, 93879, S26476962
FALSE, 93880, ['S26489168', 'S26492527', 'S26492528']"""

with io.StringIO(text) as f:
    for line in f:
        print(re.split("(?<!'), (?!')", line.strip()))


# ['TRUE', '93877', 'S26476961']
# ['TRUE', '93878', "['S26489167', 'S26492524']"]
# ['FALSE', '93879', 'S26476962']
# ['FALSE', '93880', "['S26489168', 'S26492527', 'S26492528']"]

# Or with pandas

with io.StringIO(text) as f:
    print(pd.read_csv(f,
                  header=None,
                  sep="(?<!'), (?!')",
                  engine='python'))

#        0      1                                        2
# 0   True  93877                                S26476961
# 1   True  93878               ['S26489167', 'S26492524']
# 2  False  93879                                S26476962
# 3  False  93880  ['S26489168', 'S26492527', 'S26492528']

编辑2:

如果您不希望依赖引号字符'的存在,可以尝试以下方法:

import ast
import re


with io.StringIO(text) as f:
    for line in f:
        parts = re.split(", (?=\[)", line.strip())
        line = []
        for part in parts:
            if all(char in part for char in ('[]')):
                line.append(ast.literal_eval(part))
            else:
                line += part.split(", ")
        print(line)

您将在左方括号之前的逗号上进行分割,然后按以下方式检查结果:
  1. 检查从分割操作生成的列表的每个元素是否具有方括号。如果是,则使用 ast.literal_eval 将其转换为 list 对象,并将其添加到最终输出列表中。
  2. 否则,在逗号上分割并将随后的列表添加到最终输出列表中。
这应该得到与上面的解决方案相同的结果。
我希望这可以帮助您。

我正在尝试这个,但返回了以下错误... Traceback (most recent call last): File "C:/path/To/script.py", line 11, in <module> with io.StringIO(text) as f: TypeError: initial_value must be unicode or None, not str - jbchurchill
1
你必须使用Python2。请查看更改。 - Abdou
1
太棒了!在Python 3中可以工作。我从未见过像sep="(?<!'), (?!')"这样的好主意。 - cardamom

2

这里有一个有趣的解决方案:将每一行按逗号分割,并尝试将每个部分解析为Python代码。如果无效,则意味着我们没有包含整个列表:

import ast

def get_columns(line):
    def valid(code):
        try:
            ast.parse(code.strip())
        except SyntaxError:
            return False
        return True
    sections = line.split(',')
    columns = []
    for i, section in enumerate(sections):
        if i == len(sections) - 1 or valid(section):
            columns.append(section)
        else:
            sections[i + 1] = ','.join([section, sections[i + 1]])
    return columns

with open(inFile) as f:
    for line in f:
        for column in get_columns(line):
            print(column)

由于这并不涉及到您的确切情况的正则表达式,因此这是非常通用的。还要注意的是,由于数据文件中的“代码”都没有被评估来执行测试,所以这是完全安全的。

适用于Python 2和3。


尝试这一个也。当前在else行收到IndexError错误... for column in get_columns(line): - jbchurchill
你提供的文本数据肯定不是这个意思吧? - jmd_dk
是的,现在似乎打印出了所有内容。但我仍然无法将那个看起来像列表的字符串隔离出来,并使其像列表一样工作。不过这很有帮助。 - jbchurchill
1
获取列表的一种简单而不太规范的方法是检查 [ 字符并使用 evalif column[0] == '[': my_list = eval(column) - jmd_dk

0
这是一种反向方法。它通过检查行中的项是否以前导符号[和尾随符号]开头,来查找由csv.reader返回的数据中的列表。
import csv 

def find_lists(row):
    sublist = []
    for item in row:
        if not sublist:
            if item.startswith('['):
                if item.endswith(']'):
                    yield [item[1:-1]]
                else:
                    sublist.append(item[1:])
            else:
                yield item
        else:
            if item.endswith(']'):
                sublist.append(item[:-1])
                yield sublist
                sublist = []
            else:
                sublist.append(item)
    for item in sublist:
        yield item

with open('test.csv') as infile:
    reader = csv.reader(infile, skipinitialspace=True)
    for row in reader:
        print(list(find_lists(row)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接