วิธีแปลงการแสดงสตริงของรายการเป็นรายการ?


531

ฉันสงสัยว่าวิธีที่ง่ายที่สุดคือการแปลงstringรายการเช่นต่อไปนี้เป็นlist:

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

แม้ในกรณีที่ผู้ใช้ใส่ช่องว่างระหว่างเครื่องหมายจุลภาคและช่องว่างภายในเครื่องหมายคำพูด ฉันต้องจัดการกับเรื่องนี้เช่นกันเพื่อ:

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

ใน Python

ฉันรู้ว่าฉันสามารถตัดช่องว่างด้วยstrip()และsplit()ใช้ตัวดำเนินการแยกและตรวจสอบตัวอักษรที่ไม่ใช่ แต่รหัสก็เริ่มแย่มาก มีฟังก์ชั่นด่วนที่ฉันไม่ทราบหรือไม่?


4
คุณพยายามทำอะไรจริงๆ อาจเป็นวิธีที่ดีกว่าการพยายามแปลงไวยากรณ์รายการหลามเป็นรายการที่เกิดขึ้นจริง ...
นิโคลัสอัศวิน

1
คุณใช้ Python เวอร์ชันใด
Mark Byers

2
@Nicholas Knight: ฉันพยายามจัดการอินพุตของผู้ใช้ในแอปรุ่นเก่าที่มีการป้อนรายการทั้งหมดเป็นรายการ unicode ด้วยวงเล็บสี่เหลี่ยม @ Mark Byers ผมใช้หลาม 2.6 ดังนั้นวิธี ast.literal ทำงานได้ดีที่สุด
harijay

คำตอบ:


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 :

ด้วย ast.literal_eval คุณสามารถประเมินโหนดการแสดงออกหรือสตริงที่มีการแสดงออก Python สตริงหรือโหนดที่จัดเตรียมอาจประกอบด้วยโครงสร้างตัวอักษร Python ดังต่อไปนี้เท่านั้น: สตริงตัวเลขสิ่งอันดับรายการ dicts บูลีนและไม่มี


6
ต่อความคิดเห็นด้านล่างนี้เป็นสิ่งที่อันตรายเพราะมันจะเรียกใช้สิ่งที่หลามอยู่ในสตริง ดังนั้นหากมีคนโทรออกเพื่อลบทุกอย่างในนั้นก็จะเป็นสุข
Paul Kenjora

16
@PaulKenjora: คุณคิดว่าการไม่ได้eval ast.literal_eval
user2357112 รองรับ Monica

19
ast.literal_evalเป็นความปลอดภัยมากขึ้นกว่าevalแต่มันก็ไม่จริงปลอดภัย ตามที่เอกสารล่าสุดอธิบาย: "คำเตือนเป็นไปได้ที่จะทำให้ตัวแปลภาษา Python มีสตริงขนาดใหญ่ / ซับซ้อนเพียงพอเนื่องจากข้อ จำกัด เชิงลึกของกองซ้อนในตัวแปล AST ของ Python" ในความเป็นจริงมันอาจเป็นไปได้ที่จะเรียกใช้รหัสโดยพลการผ่านการโจมตีกองซ้อนอย่างระมัดระวังแม้ว่าเท่าที่ฉันรู้ว่าไม่มีใครสร้างหลักฐานของแนวคิดในที่สาธารณะ
abarnert

ดี แต่จะทำอย่างไรถ้ารายการไม่มีคำพูด? เช่น [4 ของ B, 1 ของ G]
sqp_125

84

jsonโมดูลเป็นทางออกที่ดีกว่าเมื่อมีการstringifiedรายการของพจนานุกรม json.loads(your_data)ฟังก์ชั่นสามารถใช้ในการแปลงเป็นรายการ

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

เหมือนกับ

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

แต่ฉันไม่ต้องการรายการที่ส่งคืนในรูปแบบ Unicode แต่ดูเหมือนว่าแม้ว่าฉันจะลบ u '' ออกจากสตริงมันก็ยังคงถือว่าข้อมูลเป็นยูนิโค้ด
Mansoor Akram

7
มันใช้งานได้กับ ints แต่ไม่ใช่สำหรับสตริงในกรณีของฉันเพราะแต่ละสตริงมีการยกมาเดี่ยวไม่ได้ยกมาสองครั้งถอนหายใจ
Paul Kenjora

4
เป็นต่อ @ คิดเห็น PaulKenjora ก็ทำงานให้แต่ไม่ได้สำหรับ'["a","b"]' "['a','b']"
Skippy le Grand Gourou

83

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 ตัวอย่างเช่นไม่สามารถจัดการเครื่องหมายคำพูดที่ใช้ Escape ในสตริงได้อย่างถูกต้อง แต่มันเรียบง่ายไม่เกี่ยวข้องกับหลักฐานอันตรายและอาจดีพอสำหรับวัตถุประสงค์ของคุณถ้าคุณอยู่บนงูหลามรุ่นเก่า


คุณช่วยบอกฉันหน่อยได้ไหมว่าทำไมคุณถึงพูดว่า " evalอันตราย" - คุณไม่ควรสั่งให้ผู้ใช้ป้อนข้อมูล " ฉันใช้ 3.6
Aaryan Dewan

1
@AaryanDewan หากคุณใช้evalโดยตรงมันจะประเมินการแสดงออกของงูหลามที่ถูกต้องซึ่งอาจเป็นอันตรายได้ literal_evalแก้ปัญหานี้โดยการประเมินโครงสร้างตัวอักษรของ Python เท่านั้น: สตริง, ตัวเลข, สิ่งอันดับ, รายการ, dicts, booleans และไม่มี
Abhishek Menon


10

มีวิธีแก้ปัญหาอย่างรวดเร็ว:

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

ช่องว่างที่ไม่ต้องการในองค์ประกอบรายการอาจถูกลบด้วยวิธีนี้:

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

นี้จะยังคงรักษาช่องว่างภายในคำพูด
ติดสาย

17
นี่เป็นคำเชิญที่เปิดกว้างสำหรับการใช้รหัสโดยอำเภอใจไม่เคยทำสิ่งนี้หรืออะไรทำนองนั้นเว้นแต่คุณจะรู้ด้วยความมั่นใจแน่นอนว่าข้อมูลนั้นจะเชื่อถือได้ 100%
Nicholas Knight

1
ฉันสามารถใช้คำแนะนำนี้ได้เพราะฉันรู้ว่าข้อมูลของฉันจะอยู่ในรูปแบบนั้นเสมอและเป็นงานการประมวลผลข้อมูล
Manish Ranjan

9

ได้รับแรงบันดาลใจจากคำตอบบางข้อข้างต้นที่ใช้งานได้กับแพ็กเกจหลามพื้นฐานฉันเปรียบเทียบประสิทธิภาพของการใช้งานบางอย่าง (โดยใช้ Python 3.7.3):

วิธีที่ 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

วิธีที่ 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

วิธีที่ 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

ฉันรู้สึกผิดหวังที่เห็นสิ่งที่ฉันพิจารณาวิธีที่มีความสามารถในการอ่านที่แย่ที่สุดคือวิธีที่มีประสิทธิภาพดีที่สุด ... มีข้อควรพิจารณาเมื่อเลือกตัวเลือกที่อ่านได้มากที่สุด ... สำหรับประเภทของงานที่ฉันใช้กับงูใหญ่ ความสามารถในการอ่านค่ามากกว่าตัวเลือกที่มีประสิทธิภาพมากกว่าเล็กน้อย แต่ตามปกติมันขึ้นอยู่กับ


9

หากเป็นเพียงรายการมิติเดียวสามารถทำได้โดยไม่ต้องนำเข้าอะไร:

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

8
ข้อควรระวัง: สิ่งนี้อาจเป็นอันตรายได้หากสตริงใด ๆ ในรายการมีเครื่องหมายจุลภาคอยู่ระหว่างนั้น
Hassan Kamal

สิ่งนี้จะไม่ทำงานหากรายการสตริงของคุณเป็นรายการ
crypdick

@crypdick จุดดีเพิ่มหมายเหตุเกี่ยวกับว่า :)
ruohola

6

สมมติว่าอินพุตทั้งหมดของคุณเป็นรายการและการอ้างอิงราคาคู่ในอินพุตนั้นไม่สำคัญเลยซึ่งสามารถทำได้ด้วยการแทนที่ regexp แบบง่าย มันค่อนข้าง perl-y แต่ใช้งานได้เหมือนมีเสน่ห์ โปรดทราบว่าตอนนี้เอาต์พุตเป็นรายการของสตริง 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 มีการคอมไพล์ regexp (สำหรับความเร็ว) ของตัวละครทั้งหมดที่เราไม่ต้องการใช้] เป็นตัวละครที่ต้องการเล่ห์เหลี่ยมแบ็กสแลช re.sub แทนที่ตัวละครเหล่านี้ทั้งหมดโดยไม่มีอะไรเลยและเราแยกสตริงผลลัพธ์ที่เครื่องหมายจุลภาค

โปรดทราบว่าสิ่งนี้จะลบช่องว่างออกจากรายการภายในด้วย u '["oh no"]' ---> [u'ohno '] หากนี่ไม่ใช่สิ่งที่คุณต้องการ regexp จะต้องได้รับความนิยมเพิ่มขึ้นเล็กน้อย


4

หากคุณรู้ว่ารายการของคุณมีเพียงสตริงที่ยกมาตัวอย่าง pyparsing นี้จะให้รายชื่อของคุณของสตริงที่ถูกปล้น (แม้จะรักษา Unicode เดิม)

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

หากรายการของคุณสามารถมีประเภทข้อมูลมากขึ้นหรือแม้กระทั่งมีรายการภายในรายการคุณจะต้องมีไวยากรณ์ที่สมบูรณ์มากขึ้น - เช่นนี้ใน wiki แบบ pyparsing ซึ่งจะจัดการ tuples, รายการ, ints, ลอยและสตริงที่ยกมา จะทำงานกับ Python เวอร์ชั่นได้ถึง 2.4


คุณจะให้ฉันรู้วิธีการใช้ "parseString (). asList ()" ถ้าฉันมีสตริงประเภทนี้: '["A", "B", "C", "C", ["D"]]' ตามที่คุณ ได้ระบุว่าการทำ pyparsing สามารถทำได้เช่นกัน แต่ดูเหมือนจะไม่พบวิธีที่ถูกต้องที่จะทำ
Mansoor Akram

"หากรายการของคุณมีประเภทข้อมูลมากขึ้นหรือแม้กระทั่งมีรายการอยู่ในรายการคุณจะต้องใช้ไวยากรณ์ที่สมบูรณ์ยิ่งขึ้น" - โปรดดูลิงก์ที่ฉันให้ไว้ในคำตอบสำหรับตัวแยกวิเคราะห์ที่จะจัดการรายการที่ซ้อนกันและประเภทข้อมูลอื่น ๆ
PaulMcG

การวาง Pyparsing ไม่ได้อยู่ใน wikispaces แล้ว parsePythonValue.pyตัวอย่างเช่นขณะนี้อยู่ใน GitHub ที่github.com/pyparsing/pyparsing/blob/master/examples/...
PaulMcG

1

เพื่อให้คำตอบของ @Ryan ให้สมบูรณ์ยิ่งขึ้นโดยใช้ json หนึ่งฟังก์ชันที่สะดวกมากในการแปลงยูนิโค้ดคือฟังก์ชันที่โพสต์ไว้ที่นี่: https://stackoverflow.com/a/13105359/7599285

เช่นด้วยเครื่องหมายคำพูดคู่หรือเดี่ยว:

>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

ฉันต้องการมอบโซลูชันการจัดรูปแบบที่ใช้งานง่ายด้วย regex ฟังก์ชั่นด้านล่างใช้เป็นรายการสตริงที่มีสตริงที่กำหนดเอง

คำอธิบายแบบขั้นตอน: คุณลบ whitespacing, bracketing และ value_separators ทั้งหมด (หากไม่ได้เป็นส่วนหนึ่งของค่าที่คุณต้องการแตกข้อมูลมิฉะนั้นจะทำให้ regex ซับซ้อนขึ้น) จากนั้นคุณแยกสตริงที่ทำความสะอาดด้วยเครื่องหมายคำพูดเดี่ยวหรือคู่และนำค่าที่ไม่ว่างเปล่า (หรือค่าดัชนีที่แปลก ๆ

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

คุณอาจพบปัญหาดังกล่าวในขณะที่จัดการกับข้อมูลที่ถูกคัดลอกเก็บไว้เป็น Pandas DataFrame

การแก้ปัญหานี้ทำงานเช่นเสน่ห์ถ้ารายการของค่าที่เป็นปัจจุบันเป็นข้อความ

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

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

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

ไม่ต้องใช้ห้องสมุดภายนอก


-1

ดังนั้นต่อไปนี้คำตอบทั้งหมดที่ฉันตัดสินใจที่จะกำหนดเวลาวิธีการที่พบมากที่สุด:

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

ดังนั้นในที่สุด regex ชนะ!


-1

คุณสามารถบันทึก. strip () fcn ด้วยตัวเองโดยเพียงแค่ตัดอักขระตัวแรกและตัวสุดท้ายออกจากการแสดงสตริงของรายการ (ดูบรรทัดที่สามด้านล่าง)

>>> 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'>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.