简体   繁体   中英

python - fill dictionary with a string

I have a string composed of multiple lines each containing a key and 2 values of 2 attributes of an object. I want to load them in a dictionary and the string is located in a txt file. I was able to only go as far as defining the function:

def load_a_string(self, thestring):

Here is what the string looks like in the txt file(the string i want starts at the fourth line) :

noir
False
None
((3, 0), 'blanc', 'pion')
((5, 4), 'blanc', 'pion')
((2, 1), 'noir', 'pion')
((2, 5), 'noir', 'pion')
((7, 2), 'blanc', 'pion')
((1, 2), 'noir', 'pion')
((6, 7), 'blanc', 'pion')
((7, 6), 'blanc', 'pion')
((6, 3), 'blanc', 'pion')
((5, 6), 'blanc', 'pion')
((5, 0), 'noir', 'pion')
((0, 1), 'noir', 'pion')
((3, 2), 'blanc', 'pion')
((2, 3), 'noir', 'pion')
((0, 7), 'noir', 'pion')
((1, 0), 'noir', 'pion')
((6, 5), 'blanc', 'pion')
((2, 7), 'noir', 'pion')
((7, 0), 'blanc', 'pion')
((6, 1), 'blanc', 'pion')
((7, 4), 'blanc', 'pion')
((0, 5), 'noir', 'pion')
((3, 4), 'noir', 'pion')

Starting from the 4th line is what i want to convert into the dict. Every number tuples is a key in the dict and the other two are attributes of an instance(object) of a class called piece, 'blanc' or 'noir' are value of the attribute piece.color and 'pion' is a value of the attribute piece.thetype (it's other possible value is 'dame'). Basically if i want to fill the dict manually like above it's like this:

self.cases = {}
self.cases[(3, 0)] = Piece("blanc", "pion")
self.cases[(5, 4)] = Piece("blanc", "pion")
self.cases[(2, 1)] = Piece("noir", "pion")
...

The function I'm making take a string as a parameter to fill the dict. This function is meant to be used in another function that will read a txt file like above and find the string in the file to use it as the parameter for this function. So i'm also wondering how to find the string in a txt file like the one above so i can pass it to this function. That last part will be in the other function. There is probably an easier way to do this but i really need to do it this way so everything fits together.

Edit: Yes, this is really the structure/format and unfortunately i cannot change it.

If that is really format, simplest way would be

rows = [x for x in open('file.ext', 'r')][3:]

for x in rows:
   key, color, thetype = eval(x)
   dict[key] = Piece(color, thetype)

If that file was generated by Python and you have access to the program used to generate it, or can induce the person who does have access, you should consider using the pickle module to store and save representations of Python data.

If you can't use a more reliable storage mechanism and if the data is exactly as represented in your example, then you can do something like this for each line:

 line = line.translate(None, '()')
 terms = line.split(',')
 self.cases[(terms[0], terms[1]) = Piece(terms[2], terms[3])

If the input is safe (it comes from a trusted party), you can use eval, which takes a string with Python code, evaluates it, and returns the result.

For instance:

from __future__ import print_function
from collections import namedtuple
from pprint import pprint
import sys

# Read the entire file to a list of lines
with open('my_text.txt', 'r') as f:
    lines = f.readlines()

# Declare a Piece class, which is a named tuple (immutable)
Piece = namedtuple('Piece', ['color', 'piece'])

# The cases dictionary where we will write
cases = {}

# For lines 4 to last, counting them starting at 4...
for num_line, line in enumerate(lines[3:], start=4):
    try:
        # Evaluate the line (will return a tuple)
        a_tuple = eval(line)

        # Separate the first element from the rest
        key, params = a_tuple[0], a_tuple[1:]

        # Write in the dictionary. *params is substituted with an argument for
        # each element in the tuple params.
        cases[key] = Piece(*params)
    except:
        # If something was wrong, print the line that failed in the text file
        # and raise the exception to get the traceback and stop the program.
        print("Failed to parse line %d: %s" % (num_line, line), file=sys.stderr)
        raise

# Pretty print the result
pprint(cases)

A pure Python string solution:

txt="""\
noir
False
None
((3, 0), 'blanc', 'pion')
((5, 4), 'blanc', 'pion')
((2, 1), 'noir', 'pion')
((2, 5), 'noir', 'pion')
((7, 2), 'blanc', 'pion')
((1, 2), 'noir', 'pion')
((6, 7), 'blanc', 'pion')
((7, 6), 'blanc', 'pion')
((6, 3), 'blanc', 'pion')
((5, 6), 'blanc', 'pion')
((5, 0), 'noir', 'pion')
((0, 1), 'noir', 'pion')
((3, 2), 'blanc', 'pion')
((2, 3), 'noir', 'pion')
((0, 7), 'noir', 'pion')
((1, 0), 'noir', 'pion')
((6, 5), 'blanc', 'pion')
((2, 7), 'noir', 'pion')
((7, 0), 'blanc', 'pion')
((6, 1), 'blanc', 'pion')
((7, 4), 'blanc', 'pion')
((0, 5), 'noir', 'pion')
((3, 4), 'noir', 'pion')"""

d={}
for line in txt.splitlines()[3:]:
    data=line.strip()[1:-1].split(',')
    d[line.partition(')')[0][1:]+')']=''.join(data[2:])

Or you can use literal_eval from ast:

from ast import literal_eval

d={}
for line in txt.splitlines()[3:]:
    data=literal_eval(line)
    d[data[0]]=data[1:]

In either case:

>>> d   
{(3, 0): ('blanc', 'pion'), (3, 2): ('blanc', 'pion'), (2, 1): ('noir', 'pion'), (2, 5): ('noir', 'pion'), (7, 2): ('blanc', 'pion'), (1, 2): ('noir', 'pion'), (6, 7): ('blanc', 'pion'), (7, 6): ('blanc', 'pion'), (6, 3): ('blanc', 'pion'), (5, 6): ('blanc', 'pion'), (5, 0): ('noir', 'pion'), (2, 7): ('noir', 'pion'), (5, 4): ('blanc', 'pion'), (2, 3): ('noir', 'pion'), (0, 7): ('noir', 'pion'), (1, 0): ('noir', 'pion'), (6, 5): ('blanc', 'pion'), (0, 1): ('noir', 'pion'), (7, 0): ('blanc', 'pion'), (6, 1): ('blanc', 'pion'), (7, 4): ('blanc', 'pion'), (0, 5): ('noir', 'pion'), (3, 4): ('noir', 'pion')}'blanc' 'pion'", '(1, 0)': " 'noir' 'pion'", '(1, 2)': " 'noir' 'pion'", '(6, 1)': " 'blanc' 'pion'", '(7, 0)': " 'blanc' 'pion'", '(2, 5)': " 'noir' 'pion'", '(5, 6)': " 'blanc' 'pion'", '(7, 6)': " 'blanc' 'pion'", '(5, 0)': " 'noir' 'pion'", '(7, 4)': " 'blanc' 'pion'", '(7, 2)': " 'blanc' 'pion'"}

Here is a simple way to do it using regex to extract your data like this:

import re

f = open('data','r')
data = f.read()
f.close()

text = data.split('\n')
dict = {}
for line in text:
    key = re.findall(r"\((\(\d\,\s\d\)),", line)
    attr1 = re.findall(r",\s'(\w+)',", line)
    attr2 = re.findall(r",\s'(\w+)'\)", line)
    if len(key)>0:
        dict[key[0]] = (attr1[0], attr2[0])
print dict

this will deal with any case of your data in the lines inside the file, and catch only the wanted forms of data, and you don't have to worry about wrong formated or empty lines, the output will be:

{'(3, 0)': ('blanc', 'pion'), '(3, 4)': ('noir', 'pion'), '(2, 7)': ('noir', 'pion'), '(2, 1)': ('noir', 'pion'), '(3, 2)': ('blanc', 'pion'), '(2, 3)': ('noir', 'pion'), '(0, 1)': ('noir', 'pion'), '(0, 7)': ('noir', 'pion'), '(0, 5)': ('noir', 'pion'), '(6, 3)': ('blanc', 'pion'), '(6, 5)': ('blanc', 'pion'), '(5, 4)': ('blanc', 'pion'), '(6, 7)': ('blanc', 'pion'), '(1, 0)': ('noir', 'pion'), '(1, 2)': ('noir', 'pion'), '(6, 1)': ('blanc', 'pion'), '(7, 0)': ('blanc', 'pion'), '(2, 5)': ('noir', 'pion'), '(5, 6)': ('blanc', 'pion'), '(7, 6)': ('blanc', 'pion'), '(5, 0)': ('noir', 'pion'), '(7, 4)': ('blanc', 'pion'), '(7, 2)': ('blanc', 'pion')}

hope this was a help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM