Wie konvertiere ich die Zeichenfolgendarstellung einer Liste in eine Liste?

531

Ich habe mich gefragt, was der einfachste Weg ist, eine stringListe wie die folgende in eine zu konvertieren list:

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

Auch für den Fall, dass der Benutzer Leerzeichen zwischen die Kommas und Leerzeichen innerhalb der Anführungszeichen setzt. Ich muss auch damit umgehen, um:

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

in Python.

Ich weiß, dass ich mit strip()und split()mit dem Split-Operator Leerzeichen entfernen und nach Nicht-Alphabeten suchen kann. Aber der Code wurde sehr klobig. Gibt es eine schnelle Funktion, die mir nicht bekannt ist?

Harijay
quelle
4
Was versuchst du eigentlich zu erreichen? Es gibt wahrscheinlich einen weitaus besseren Weg als zu versuchen, die Python-Listensyntax in eine tatsächliche Liste umzuwandeln ...
Nicholas Knight
1
Welche Version von Python verwenden Sie?
Mark Byers
2
@Nicholas Knight: Ich versuche, Benutzereingaben in einer Legacy-App zu verarbeiten, in der alle Listen als Unicode-Listen mit quadratischen Klammern eingegeben wurden. @ Mark Byers, ich benutze Python 2.6, so dass der ast.literal Ansatz am besten funktioniert
Harijay

Antworten:

769
>>> import ast
>>> x = u'[ "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 :

Mit ast.literal_eval können Sie einen Ausdrucksknoten oder eine Zeichenfolge, die einen Python-Ausdruck enthält, sicher auswerten. Die bereitgestellte Zeichenfolge oder der bereitgestellte Knoten besteht möglicherweise nur aus den folgenden Python-Literalstrukturen: Zeichenfolgen, Zahlen, Tupel, Listen, Diktate, Boolesche Werte und Keine.

Gemeinschaft
quelle
6
Laut Kommentar unten ist dies gefährlich, da einfach die Python ausgeführt wird, die sich in der Zeichenfolge befindet. Wenn also jemand anruft, um alles dort zu löschen, wird es glücklich sein.
Paul Kenjora
16
@PaulKenjora: Du denkst evalnicht daran ast.literal_eval.
user2357112 unterstützt Monica
19
ast.literal_evalist sicherer als eval, aber es ist nicht wirklich sicher . In neueren Versionen der Dokumente wird Folgendes erklärt: "Warnung Es ist möglich, den Python-Interpreter aufgrund von Einschränkungen der Stapeltiefe im AST-Compiler von Python mit einer ausreichend großen / komplexen Zeichenfolge zum Absturz zu bringen." Es kann tatsächlich möglich sein, beliebigen Code über einen vorsichtigen Stack-Smashing-Angriff auszuführen, obwohl meines Wissens niemand einen öffentlichen Proof of Concept dafür erstellt.
Abarnert
Nun, aber was tun, wenn die Liste keine Anführungszeichen enthält? zB [4 von B, 1 von G]
sqp_125
84

Das jsonModul ist eine bessere Lösung , wenn es eine ist Zeichenfolge Liste der Wörterbücher. Die json.loads(your_data)Funktion kann verwendet werden, um es in eine Liste zu konvertieren.

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

Ähnlich

>>> x = u'[ "A","B","C" , {"D":"E"}]'
>>> json.loads(x)
[u'A', u'B', u'C', {u'D': u'E'}]
Ryan
quelle
Ich möchte jedoch nicht, dass die zurückgegebene Liste im Unicode-Format vorliegt. Aber selbst wenn ich u '' aus dem String entferne, werden die Daten immer noch als Unicode behandelt.
Mansoor Akram
7
Dies funktioniert in meinem Fall für Ints, aber nicht für Strings, da jeder String einfach und nicht doppelt zitiert ist, seufz.
Paul Kenjora
4
Gemäß dem Kommentar von @ PaulKenjora funktioniert es für, '["a","b"]'aber nicht für "['a','b']".
Skippy le Grand Gourou
83

Das evalist gefährlich - Sie sollten keine Benutzereingaben ausführen.

Wenn Sie 2.6 oder neuer haben, verwenden Sie ast anstelle von eval:

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

Sobald Sie das haben, stripdie Saiten.

Wenn Sie eine ältere Version von Python verwenden, können Sie mit einem einfachen regulären Ausdruck dem, was Sie wollen, sehr nahe kommen:

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

Dies ist nicht so gut wie die ast-Lösung. Beispielsweise werden maskierte Anführungszeichen in Zeichenfolgen nicht korrekt verarbeitet. Aber es ist einfach, beinhaltet keine gefährliche Bewertung und ist möglicherweise gut genug für Ihren Zweck, wenn Sie auf einem älteren Python ohne Ast arbeiten.

Mark Byers
quelle
Könnten Sie mir bitte sagen, warum Sie gesagt haben: "Das evalist gefährlich - Sie sollten keine Benutzereingaben ausführen." Ich benutze 3.6
Aaryan Dewan
1
@AaryanDewan Wenn Sie evaldirekt verwenden, wird jeder gültige Python-Ausdruck ausgewertet, der möglicherweise gefährlich ist. literal_evalLöst dieses Problem, indem nur Python-Literalstrukturen ausgewertet werden: Zeichenfolgen, Zahlen, Tupel, Listen, Diktate, Boolesche Werte und Keine.
Abhishek Menon
14
import ast
l = ast.literal_eval('[ "A","B","C" , " D"]')
l = [i.strip() for i in l]
tosh
quelle
10

Es gibt eine schnelle Lösung:

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

Unerwünschte Leerzeichen in den Listenelementen können auf folgende Weise entfernt werden:

x = [x.strip() for x in eval('[ "A","B","C" , " D"]')]
Alexei Sholik
quelle
Dies würde immer noch die Leerzeichen in den Anführungszeichen
bewahren
17
Dies ist eine offene Einladung zur Ausführung von beliebigem Code. Tun Sie dies NIEMALS oder ähnliches, es sei denn, Sie wissen mit absoluter Sicherheit, dass die Eingabe immer zu 100% vertrauenswürdig ist.
Nicholas Knight
1
Ich könnte diesen Vorschlag verwenden, weil ich wusste, dass meine Daten immer in diesem Format vorliegen würden und eine Datenverarbeitungsarbeit waren.
Manish Ranjan
9

Inspiriert von einigen der obigen Antworten, die mit Basis-Python-Paketen funktionieren, habe ich die Leistung einiger weniger verglichen (mit Python 3.7.3):

Methode 1: 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

Methode 2: 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

Methode 3: kein Import

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

Ich war enttäuscht zu sehen, dass die Methode mit der schlechtesten Lesbarkeit die Methode mit der besten Leistung war. Bei der am besten lesbaren Option sind Kompromisse zu berücksichtigen. Für die Art der Workloads, für die ich normalerweise Python verwende Wert Lesbarkeit über eine etwas leistungsfähigere Option, aber wie üblich hängt es davon ab.

Kinzleb
quelle
9

Wenn es sich nur um eine eindimensionale Liste handelt, können Sie dies tun, ohne etwas zu importieren:

>>> x = u'[ "A","B","C" , " D"]'
>>> ls = x.strip('[]').replace('"', '').replace(' ', '').split(',')
>>> ls
['A', 'B', 'C', 'D']
Ruohola
quelle
8
Warnhinweis: Dies kann möglicherweise gefährlich sein, wenn eine der Zeichenfolgen in der Liste ein Komma dazwischen enthält.
Hassan Kamal
Dies funktioniert nicht, wenn Ihre Zeichenfolgenliste eine Liste von Listen ist
crypdick
@crypdick Guter Punkt, habe einen Hinweis dazu hinzugefügt :)
ruohola
6

Angenommen, alle Ihre Eingaben sind Listen und die doppelten Anführungszeichen in der Eingabe spielen keine Rolle, kann dies durch einfaches Ersetzen von regulären Ausdrücken erfolgen. Es ist ein bisschen perl-y, funktioniert aber wie ein Zauber. Beachten Sie auch, dass die Ausgabe jetzt eine Liste von Unicode-Zeichenfolgen ist. Sie haben nicht angegeben, dass Sie dies benötigen, aber es scheint angesichts der Unicode-Eingabe sinnvoll zu sein.

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

Die Junkers-Variable enthält einen kompilierten regulären Ausdruck (für die Geschwindigkeit) aller Zeichen, die wir nicht wollen, wobei] als Zeichen einige Backslash-Tricks erfordert. Das re.sub ersetzt alle diese Zeichen durch nichts, und wir teilen die resultierende Zeichenfolge durch Kommas auf.

Beachten Sie, dass dadurch auch Leerzeichen aus den Einträgen u '["oh nein"]' ---> [u'ohno '] entfernt werden. Wenn dies nicht das ist, was Sie wollten, muss der reguläre Ausdruck etwas aufgemotzt werden.

Dirkjot
quelle
4

Wenn Sie wissen, dass Ihre Listen nur Zeichenfolgen in Anführungszeichen enthalten, erhalten Sie in diesem Pyparsing-Beispiel eine Liste der entfernten Zeichenfolgen (wobei sogar die ursprüngliche Unicode-Ness erhalten bleibt).

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

Wenn Ihre Listen mehr Datentypen haben können oder sogar Listen in Listen enthalten, benötigen Sie eine vollständigere Grammatik - wie diese im Pyparsing-Wiki, die Tupel, Listen, Ints, Floats und Zeichenfolgen in Anführungszeichen verarbeitet. Funktioniert mit Python-Versionen bis 2.4.

PaulMcG
quelle
Würden Sie mich wissen lassen, wie man "parseString (). asList ()" verwendet, wenn ich diese Art von Zeichenfolge habe: '["A", "B", "C", ["D"]]' wie Sie haben festgestellt, dass Pyparsing das auch kann. aber o scheint nicht den richtigen Weg gefunden zu haben.
Mansoor Akram
"Wenn Ihre Listen mehr Datentypen haben können oder sogar Listen in Listen enthalten, benötigen Sie eine vollständigere Grammatik" - siehe den Link, den ich in meiner Antwort für einen Parser angegeben habe, der verschachtelte Listen und verschiedene andere Datentypen verarbeitet.
PaulMcG
Pyparsing wird nicht mehr in Wikispaces gehostet. Das parsePythonValue.pyBeispiel ist jetzt auf GitHub unter github.com/pyparsing/pyparsing/blob/master/examples/…
PaulMcG
1

Um die Antwort von @Ryan mit json weiter zu vervollständigen, ist eine sehr praktische Funktion zum Konvertieren von Unicode die hier veröffentlichte: https://stackoverflow.com/a/13105359/7599285

Ex mit doppelten oder einfachen Anführungszeichen:

>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']
CptHwK
quelle
0

Ich möchte eine intuitivere Musterlösung mit Regex anbieten. Die folgende Funktion verwendet als Eingabe eine String-Liste, die beliebige Strings enthält.

Schrittweise Erklärung: Sie entfernen alle Leerzeichen, Klammern und Werttrennzeichen (vorausgesetzt, sie sind nicht Teil der Werte, die Sie extrahieren möchten, andernfalls wird der reguläre Ausdruck komplexer). Dann teilen Sie die bereinigte Zeichenfolge in einfache oder doppelte Anführungszeichen und nehmen die nicht leeren Werte (oder ungerade indizierte Werte, unabhängig von der Präferenz).

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

Testbeispiel : "['21'," foo "'6', '0'," A "]"

Jordy Van Landeghem
quelle
0

und mit reinem Python - keine Bibliotheken importieren

[x for x in  x.split('[')[1].split(']')[0].split('"')[1:-1] if x not in[',',' , ',', ']]
Ioannis Nasios
quelle
0

Möglicherweise treten solche Probleme auf, wenn Sie mit als Pandas DataFrame gespeicherten Daten arbeiten.

Diese Lösung funktioniert wie ein Zauber, wenn die Werteliste als Text vorliegt .

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

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

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

Keine externe Bibliothek erforderlich.

Dobydx
quelle
-1

Nach all den Antworten entschied ich mich, die gängigsten Methoden zu bestimmen:

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

Am Ende gewinnt also Regex!

Pässe
quelle
-1

Sie können sich den .strip () fcn sparen, indem Sie einfach das erste und das letzte Zeichen aus der Zeichenfolgendarstellung der Liste entfernen (siehe dritte Zeile unten).

>>> 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'>
JCMontalbano
quelle