【问题标题】:Find list within a string在字符串中查找列表
【发布时间】:2017-08-02 17:40:32
【问题描述】:

我看到很多在字符串中查找字符串或在列表中查找字符串的示例,但是如何在字符串中查找列表。例如,我有一个包含数据列的 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] 返回左括号 ([),但我想将其放入列表中。

【问题讨论】:

  • 使用csv
  • 另外,考虑使用pandas
  • 尝试在逗号后面不跟':[re.split("(?<!'), (?!')", line) for line in open(filename)]分开。
  • (a) 之后你想对数据做什么? (b) 例如,将第一行设为:TRUE, 93877, ['S26476961'] 或者换句话说,所有行都以列表结尾,你会怎么说?
  • @BillBell 如果最后一列的所有值都是列表(在某些情况下是单项列表),那就没问题了。

标签: python csv


【解决方案1】:

这依赖于引用列表元素的' 字符。使用此信息,它仅使用正则表达式在该字符后面或前面没有逗号的地方拆分:

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']

编辑:

如果您使用的是 python2,则需要将文本转换为 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_evallist 对象,并将其添加到最终输出列表中。
  2. 否则,以逗号分隔并将随后的列表添加到最终输出列表中。

这应该与上述解决方案相同。

我希望这会有所帮助。

【讨论】:

  • 我正在尝试这个,但它返回这个错误...Traceback (most recent call last):File "C:/path/To/script.py", line 11, in &lt;module&gt;with io.StringIO(text) as f:TypeError: initial_value must be unicode or None, not str
  • 您必须使用python2。查看更改
  • 太棒了!在 Python 3 中工作。从未见过像 sep="(?&lt;!'), (?!')" 这样的东西是个好主意..
【解决方案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):
  • 确定不是您提供的文本数据?
  • 是的,它现在似乎可以打印所有内容。我仍然无法隔离看起来像列表的字符串并使其像列表一样工作。不过还是很有帮助的。
  • 获取列表的一种廉价而肮脏的方法是检查 [ 字符并使用 eval: if column[0] == '[': my_list = eval(column)
【解决方案3】:

这是相反的方法。它通过检查行项目中的前导 [ 和尾随 ] 来查找 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)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    • 2022-07-07
    • 2011-08-31
    • 1970-01-01
    • 2023-04-03
    • 2019-11-17
    相关资源
    最近更新 更多