如何将列表的字符串表示形式转换为列表

How to convert string representation of list to a list

提问人:harijay 提问时间:12/13/2009 最后编辑:ShadowRangerharijay 更新时间:4/26/2023 访问量:700775

问:

我想知道最简单的方法是什么,将如下所示的列表的字符串表示形式转换为:list

x = '[ "A","B","C" , " D"]'

即使用户在逗号之间放置空格,在引号内放置空格,我也需要处理并将其转换为:

x = ["A", "B", "C", "D"] 

我知道我可以用 和 去掉空格,并检查非字母字符。但是代码变得非常笨拙。有没有我不知道的快速功能?strip()split()

Python 字符串 列表 文本解析

评论


答:

28赞 Alexei Sholik 12/13/2009 #1

有一个快速的解决方案:

x = eval('[ "A","B","C" , " D"]')

可以通过以下方式删除列表元素中不需要的空格:

x = [x.strip() for x in eval('[ "A","B","C" , " D"]')]

评论

1赞 tosh 12/13/2009
这仍将保留引号内的空格
35赞 Nicholas Knight 12/13/2009
这是对任意代码执行的公开邀请,永远不要这样做或类似的事情,除非您绝对确定输入将始终是 100% 可信的。
1赞 Manish Ranjan 3/12/2016
我可以使用这个建议,因为我知道我的数据总是采用这种格式,并且是一项数据处理工作。
20赞 tosh 12/13/2009 #2
import ast
l = ast.literal_eval('[ "A","B","C" , " D"]')
l = [i.strip() for i in l]
115赞 Mark Byers 12/13/2009 #3

这很危险 - 您不应该执行用户输入。eval

如果您使用的是 2.6 或更高版本,请使用 ast 而不是 eval:

>>> import ast
>>> ast.literal_eval('["A","B" ,"C" ," D"]')
["A", "B", "C", " D"]

一旦你有了它,字符串。strip

如果你使用的是旧版本的 Python,你可以通过一个简单的正则表达式非常接近你想要的东西:

>>> x='[  "A",  " B", "C","D "]'
>>> re.findall(r'"\s*([^"]*?)\s*"', x)
['A', 'B', 'C', 'D']

这不如 ast 解决方案好,例如,它不能正确处理字符串中的转义引号。但它很简单,不涉及危险的 eval,如果你使用的是没有 ast 的旧 Python,它可能足以满足你的目的。

评论

0赞 Aaryan Dewan 7/17/2017
你能告诉我为什么你说“这很危险 - 你不应该执行用户输入”吗?我正在使用 3.6eval
2赞 Abhishek Menon 9/22/2017
@AaryanDewan,如果直接使用,它将计算任何有效的 Python 表达式,这是潜在的危险。 通过仅计算 Python 文字结构来解决此问题:字符串、数字、元组、列表、字典、布尔值和 None。evalliteral_eval
1187赞 Roger Pate 12/13/2009 #4
>>> import ast
>>> x = '[ "A","B","C" , " D"]'
>>> x = ast.literal_eval(x)
>>> x
['A', 'B', 'C', ' D']
>>> x = [n.strip() for n in x]
>>> x
['A', 'B', 'C', 'D']

ast.literal_eval

计算表达式节点或仅包含 Python 文本或容器显示的字符串。提供的字符串或节点只能由以下 Python 文字结构组成:字符串、字节、数字、元组、列表、字典、集合、布尔值和 .NoneEllipsis

这可用于评估包含 Python 值的字符串,而无需自己解析值。它不能计算任意复杂的表达式,例如涉及运算符或索引。

评论

12赞 Paul Kenjora 11/19/2017
根据下面的评论,这很危险,因为它只是运行字符串中的任何 python。因此,如果有人呼吁删除其中的所有内容,它很乐意这样做。
28赞 user2357112 3/20/2018
@PaulKenjora:你在想,不是.evalast.literal_eval
38赞 abarnert 3/30/2018
ast.literal_eval更安全,但实际上并不安全。正如最新版本的文档所解释的那样:“警告 由于 Python 的 AST 编译器中的堆栈深度限制,可能会使用足够大/复杂的字符串使 Python 解释器崩溃。事实上,通过仔细的堆栈粉碎攻击来运行任意代码是可能的,尽管据我所知,没有人为此构建公开的概念证明。eval
1赞 sqp_125 4/23/2020
好吧,但是如果列表没有引号怎么办?例如 [4 of B, 1 of G]
4赞 ForceBru 6/15/2020
@sqp_125,那么它是一个常规列表,你不需要解析任何东西?
5赞 PaulMcG 12/13/2009 #5

如果您知道您的列表仅包含带引号的字符串,则此 pyparsing 示例将为您提供剥离字符串列表(甚至保留原始的 Unicode-ness)。

>>> from pyparsing import *
>>> x =u'[ "A","B","C" , " D"]'
>>> LBR,RBR = map(Suppress,"[]")
>>> qs = quotedString.setParseAction(removeQuotes, lambda t: t[0].strip())
>>> qsList = LBR + delimitedList(qs) + RBR
>>> print qsList.parseString(x).asList()
[u'A', u'B', u'C', u'D']

如果你的列表可以有更多的数据类型,甚至在列表中包含列表,那么你将需要一个更完整的语法 - 就像 pyparsing examples 目录中的这个语法一样,它将处理元组、列表、整数、浮点数和带引号的字符串。

6赞 dirkjot 12/13/2009 #6

假设您的所有输入都是列表,并且输入中的双引号实际上无关紧要,这可以通过简单的正则表达式替换来完成。它有点perl-y,但它就像一个魅力。另请注意,输出现在是 Unicode 字符串列表,您没有指定需要它,但考虑到 Unicode 输入,这似乎是有道理的。

import re
x = u'[ "A","B","C" , " D"]'
junkers = re.compile('[[" \]]')
result = junkers.sub('', x).split(',')
print result
--->  [u'A', u'B', u'C', u'D']

junkers 变量包含我们不想要的所有字符的编译正则表达式(用于速度),使用 ] 作为字符需要一些反斜杠技巧。 re.sub 将所有这些字符替换为 nothing,我们将生成的字符串拆分为逗号。

请注意,这还会删除条目 u'[“oh no”]' ---> [u'ohno'] 中的空格。如果这不是您想要的,则需要对正则表达式进行一些补充。

248赞 Ryan 2/17/2016 #7

每当有字符串化的字典列表时,json 模块是更好的解决方案。该函数可用于将其转换为列表。json.loads(your_data)

>>> import json
>>> x = '[ "A","B","C" , " D"]'
>>> json.loads(x)
['A', 'B', 'C', ' D']

同样地

>>> x = '[ "A","B","C" , {"D":"E"}]'
>>> json.loads(x)
['A', 'B', 'C', {'D': 'E'}]

评论

31赞 Paul Kenjora 11/19/2017
这适用于整数,但在我的情况下不适用于字符串,因为每个字符串都是单引号而不是双引号,唉。
30赞 Skippy le Grand Gourou 6/19/2019
根据 @PaulKenjora 的评论,它适用于但不适用于 .'["a","b"]'"['a','b']"
5赞 Eugene Chabanov 12/10/2020
就我而言,我不得不在初始字符串中用双引号替换单引号以确保它有效,但我确信该字符串中的数据不包含任何会影响最终结果的关键单引号/双引号。.replace('\'', '"')
0赞 Muhammad Yasirroni 12/12/2021
如果用户只输入数字列表,我认为这是阻止恶意用户的最安全方法。
0赞 Karl Knechtel 8/11/2022
该方法更通用。例如,JSON 无法处理字符串的前缀,因为它无法识别单独的类型。JSON 还要求字符串使用双引号。ast.literal_evalbbytes
2赞 CptHwK 4/27/2018 #8

为了使用 JSON 进一步完成 Ryan 的答案,这个答案中有一个非常方便的转换 Unicode 函数。

使用双引号或单引号的示例:

>print byteify(json.loads(u'[ "A","B","C" , " D"]')
>print byteify(json.loads(u"[ 'A','B','C' , ' D']".replace('\'','"')))
['A', 'B', 'C', ' D']
['A', 'B', 'C', ' D']

评论

0赞 Karl Knechtel 8/11/2022
这里唯一的新信息是与所提出的问题无关的进一步处理步骤,并且在大多数情况下也介于不相关和有害之间。数据通常应该被理解为字符串(2.x 中的对象),而不是字节序列。unicode
0赞 Jordy Van Landeghem 6/1/2018 #9

我想用正则表达式提供一个更直观的模式化解决方案。 以下函数将包含任意字符串的字符串化列表作为输入。

逐步说明:删除所有空格、括号和value_separators(前提是它们不是要提取的值的一部分,否则会使正则表达式更加复杂)。然后,将清理后的字符串拆分为单引号或双引号,并采用非空值(或奇数索引值,无论首选项如何)。

def parse_strlist(sl):
import re
clean = re.sub("[\[\],\s]","",sl)
splitted = re.split("[\'\"]",clean)
values_only = [s for s in splitted if s != '']
return values_only

测试样本: “['21',”foo“ '6', '0', ”A“]”

0赞 passs 8/6/2018 #10

因此,在回答了所有问题之后,我决定对最常见的方法进行计时:

from time import time
import re
import json

my_str = str(list(range(19)))
print(my_str)

reps = 100000

start = time()
for i in range(0, reps):
    re.findall("\w+", my_str)
print("Regex method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    json.loads(my_str)
print("JSON method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    ast.literal_eval(my_str)
print("AST method:\t\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    [n.strip() for n in my_str]
print("strip method:\t", (time() - start) / reps)

    regex method:     6.391477584838867e-07
    json method:     2.535374164581299e-06
    ast method:         2.4425282478332518e-05
    strip method:     4.983267784118653e-06

所以最终正则表达式赢了!

16赞 ruohola 8/28/2018 #11

如果它只是一个一维列表,则可以在不导入任何内容的情况下完成此操作:

>>> x = u'[ "A","B","C" , " D"]'
>>> ls = x.strip('[]').replace('"', '').replace(' ', '').split(',')
>>> ls
['A', 'B', 'C', 'D']

评论

16赞 Hassan Kamal 10/4/2018
注意:如果列表中的任何字符串中间有一个逗号,这可能会很危险。
0赞 crypdick 3/19/2020
如果字符串列表是列表列表,则此操作不起作用
0赞 JCMontalbano 1/9/2019 #12

你可以通过从列表的字符串表示中切掉第一个和最后一个字符来节省 .strip() 函数(参见下面的第三行):

>>> mylist=[1,2,3,4,5,'baloney','alfalfa']
>>> strlist=str(mylist)
['1', ' 2', ' 3', ' 4', ' 5', " 'baloney'", " 'alfalfa'"]
>>> mylistfromstring=(strlist[1:-1].split(', '))
>>> mylistfromstring[3]
'4'
>>> for entry in mylistfromstring:
...     print(entry)
...     type(entry)
...
1
<class 'str'>
2
<class 'str'>
3
<class 'str'>
4
<class 'str'>
5
<class 'str'>
'baloney'
<class 'str'>
'alfalfa'
<class 'str'>
29赞 kinzleb 5/1/2019 #13

受到上面一些适用于基本 Python 包的答案的启发,我比较了一些(使用 Python 3.7.3)的性能:

方法一:ast

import ast

list(map(str.strip, ast.literal_eval(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, ast.literal_eval(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import ast', number=100000)
# 1.292875313000195

方法二:json

import json
list(map(str.strip, json.loads(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, json.loads(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import json', number=100000)
# 0.27833264000014424

方法3:不导入

list(map(str.strip, u'[ "A","B","C" , " D"]'.strip('][').replace('"', '').split(',')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, u'[ \"A\",\"B\",\"C\" , \" D\"]'.strip('][').replace('\"', '').split(',')))", number=100000)
# 0.12935059100027502

我很失望地看到我认为可读性最差的方法却是性能最好的方法......在选择最易读的选项时,需要考虑权衡......对于我使用 Python 的工作负载类型,我通常更看重可读性,而不是性能稍高的选项,但像往常一样,这取决于。

评论

1赞 Is_this_my_username 11/20/2020
有什么特别的原因在前面有一个u'[ "A","B","C" , " D"]'
0赞 Karl Knechtel 8/11/2022
手动方法根本没有那么强大,而且工作量也更少,所以它更快也就不足为奇了。它不会处理字符串中的转义序列,也不会处理其他引号类型。(JSON 方法需要双引号,但会处理转义序列。它也只会处理字符串的平面列表;其他方法可以处理复杂的嵌套数据结构。
0赞 Ioannis Nasios 7/24/2019 #14

使用纯 Python - 不导入任何库:

[x for x in  x.split('[')[1].split(']')[0].split('"')[1:-1] if x not in[',',' , ',', ']]
3赞 dobydx 5/28/2020 #15

在处理存储为 Pandas DataFrame 的抓取数据时,您可能会遇到此类问题。

如果值列表以文本形式显示,则此解决方案的工作方式类似于魅力。

def textToList(hashtags):
    return hashtags.strip('[]').replace('\'', '').replace(' ', '').split(',')

hashtags = "[ 'A','B','C' , ' D']"
hashtags = textToList(hashtags)

Output: ['A', 'B', 'C', 'D']

无需外部库。

2赞 Hrvoje 4/2/2021 #16

当您将存储为字符串的列表加载到 CSV 时,通常会发生这种情况

如果您的列表以 OP 询问的形式存储在 CSV 中:

x = '[ "A","B","C" , " D"]'

以下是将其加载回列表的方法:

import csv
with open('YourCSVFile.csv') as csv_file:
    reader = csv.reader(csv_file, delimiter=',')
    rows = list(reader)

listItems = rows[0]

listItems现在是列表

评论

1赞 Tomerikoo 4/2/2021
不知道这与问题有什么关系...... 给出列表列表。每个内部列表都是 csv 列的字符串列表。一开始就没有列表的字符串表示形式......list(reader)
0赞 Hrvoje 4/2/2021
@Tomerikoo列表的字符串表示形式完全相同,只是它在文件中。
0赞 Tomerikoo 4/2/2021
不。列表的字符串表示形式是 。当您读取带有 的 csv 文件时,每行是 。这是一个字符串列表。不是列表的字符串表示形式..."['1', '2', '3']"csv.reader['1', '2', '3']
0赞 Hrvoje 4/2/2021
@Tomerikoo您将列表存储在文件中,然后在此处使用任何方法来恢复它。
1赞 Tomerikoo 4/2/2021
好吧,假设 csv 实际上就在里面。假设 csv 行是 。阅读它会给予,然后做会给.同样,我不明白这如何回答这个问题......[1, 2, 3][1,2,3] 4 5list(reader)[["[1,2,3]", "4", "5"], ...]rows[0]["[1,2,3]", "4", "5"]
12赞 born_naked 7/8/2021 #17

无需导入任何内容或进行评估。对于大多数基本用例,您可以在一行中执行此操作,包括原始问题中给出的用例。

一个衬垫

l_x = [i.strip() for i in x[1:-1].replace('"',"").split(',')]

解释

x = '[ "A","B","C" , " D"]'
# String indexing to eliminate the brackets.
# Replace, as split will otherwise retain the quotes in the returned list
# Split to convert to a list
l_x = x[1:-1].replace('"',"").split(',')

输出

for i in range(0, len(l_x)):
    print(l_x[i])
# vvvv output vvvvv
'''
 A
B
C
  D
'''
print(type(l_x)) # out: class 'list'
print(len(l_x)) # out: 4

您可以根据需要使用列表推导式来解析和清理此列表。

l_x = [i.strip() for i in l_x] # list comprehension to clean up
for i in range(0, len(l_x)):
    print(l_x[i])
# vvvvv output vvvvv
'''
A
B
C
D
'''

嵌套列表

如果你有嵌套列表,它确实会变得更烦人。不使用正则表达式(这将简化替换),并假设您要返回一个扁平化列表(并且 python 的禅宗说扁平化比嵌套更好):

x = '[ "A","B","C" , " D", ["E","F","G"]]'
l_x = x[1:-1].split(',')
l_x = [i
    .replace(']', '')
    .replace('[', '')
    .replace('"', '')
    .strip() for i in l_x
]
# returns ['A', 'B', 'C', 'D', 'E', 'F', 'G']

如果你需要保留嵌套列表,它会变得有点丑陋,但仍然可以通过正则表达式和列表推导来完成:

import re

x = '[ "A","B","C" , " D", "["E","F","G"]","Z", "Y", "["H","I","J"]", "K", "L"]'
# Clean it up so the regular expression is simpler
x = x.replace('"', '').replace(' ', '')
# Look ahead for the bracketed text that signifies nested list
l_x = re.split(r',(?=\[[A-Za-z0-9\',]+\])|(?<=\]),', x[1:-1])
print(l_x)
# Flatten and split the non nested list items
l_x0 = [item for items in l_x for item in items.split(',') if not '[' in items]
# Convert the nested lists to lists
l_x1 = [
    i[1:-1].split(',') for i in l_x if '[' in i
]
# Add the two lists
l_x = l_x0 + l_x1

最后一个解决方案将适用于存储为字符串的任何列表,无论是否嵌套。

评论

0赞 Ari Anisfeld 6/4/2022
请注意,该方法不能很好地处理空列表。你拿走并回来.如果要分析数据框中的列,这可能是一个问题。否则不错的解决方案!'[]'['']
0赞 Banane 9/6/2022
列表理解似乎比解决方案慢。可能是因为剥离操作是重复 len(x) 次而不是 1 次,并且创建了两个列表而不是 1(一个由 'split()' 返回,另一个由理解返回)。x.strip('[]').replace('"', '').split(',')
-1赞 CassAndr 10/28/2021 #18

这个解决方案比我在之前的答案中读到的一些解决方案更简单,但它需要匹配列表的所有功能。

x = '[ "A","B","C" , " D"]'
[i.strip() for i in x.split('"') if len(i.strip().strip(',').strip(']').strip('['))>0]

输出:

['A', 'B', 'C', 'D']
10赞 Tomato Master 1/28/2022 #19

你可以这样做

**

x = '[ "A","B","C" , " D"]'
print(eval(x))

** 最好的一个是公认的答案

虽然这不是一种安全的方法,但最好的答案是公认的。 在发布答案时没有意识到评估危险。

评论

3赞 born_naked 2/5/2022
不建议在此线程上的几个位置使用 eval,因为无论输入什么,它都会简单地作为代码运行,从而带来安全风险。这也是一个重复的答案。
1赞 DINA TAKLIT 3/20/2023 #20

json.loads()而 From JSON 包是 JavaScript 的等效方式,因此使用 JSON 解决方案来让生活更简单json.dumps()JSON.parse()JSON.stringify()

import json
a = '[ "A","B","C" , " D"]'
print(json.loads(a)) #['A', 'B', 'C', ' D']
b = ['A', 'B', 'C', ' D']
print(json.dumps(b)) # '["A", "B", "C", " D"]'

0赞 Shahin Shirazi 4/5/2023 #21

如果您不想导入任何库,这是另一种解决方案:

x = '[ "A","B","C" , " D"]'
def toList(stringList):
  stringList = stringList.split('[')[1]# removes "["
  stringList = stringList.split(']')[0]# removes "]"
  stringList = stringList.split(',')#gets objects in the list
  return [text.strip()[1:-1] for text in stringList] #eliminate additional " or ' in the string.
toList(x)

输出:

['A', 'B', 'C', ' D']

此方法的警告是,如果字符串中有逗号,则它不起作用,例如,如果您的输入是

x = '[ "A","B,F","C" , " D"]'

您的输出将是

['A', '', '', 'C', ' D']

这不是你想要的。