1

I have a textfile of the format (well, it is much less structured, but the parts I need follow the below idea):

[ThisShouldBeAKey]
Value1=yes
Value2=no
Value3=fancywords
Value4=numbers

[ThisShouldBeAnotherKey]
Value2=true
Value3=333
Value5=qqq

I want to convert this file into a dictionary, so that I can easily compare certain values of certain keys. E.g., retrieve Value8 and Value9 of [Key1] and [Key100].

I imagine I can retrieve Value8 and Value9 by simply iterating over a textdump of the [key], and using startswith() or other string functions to retrieve what I want. So, although you could (ideally?) make a nested dictionary, I don't >think< I necessarily require this.

However, I even have trouble simply getting a dictionary of [Key]:Value1Value2Value3AllthevaluesTextdump. I have tried a few things on my own, I have tried the suggestion here ( In Python, how do I read a file into a dictionary with multiple line entries? ) and alterations on it, but I always run into a problem with for-loops, while-loops, or the file simply not being read properly.

How would you recommend doing this?

Thank you in advance!

user2286565
  • 31
  • 1
  • 5
  • 3
    Check [configparser module from standard library](https://docs.python.org/3/library/configparser.html) – buran Mar 13 '23 at 11:12
  • Dictionaries can only have one value per key, so you would need an array to hold all the values or a nested dictionary if you want the custom indexes. – OuterSoda Mar 13 '23 at 11:12
  • 2
    Does this answer your question? [How to read and write INI file with Python3?](https://stackoverflow.com/questions/8884188/how-to-read-and-write-ini-file-with-python3) – Jorge Luis Mar 13 '23 at 11:53

2 Answers2

0

Loop on each line and detect the starting square bracket to start a new group. Use split to get key and value on other lines to update the current group:

result = dict()
for line in file:
    if line.startswith("["):
        result[line.strip("[] ")] = group = dict()
    elif "=" in line:
        group.update([line.strip().split("=",1)])

print(result)
{'ThisShouldBeAKey': {'Value1': 'yes', 'Value2': 'no', 
                      'Value3': 'fancywords', 'Value4': 'numbers'}, 
 'ThisShouldBeAnotherKey': {'Value2': 'true', 'Value3': '333',
                            'Value5': 'qqq'}}
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

You could just iterate through the lines with <dict>.setdefault and <str>.split

filepath = 'x.txt' #<-- name of / path to your file

with open(filepath, 'r') as f: fLines = f.read().splitlines() #<-- read file
kCur = '__no_parent_key__' #<-- set a default for if 1st line isn't a [key]
result = {kCur: {}} #<-- initiate

for l in fLines:
    if '=' in l: _, l = result[kCur].update([l.split('=', 1)]), ''# [l empty -> skip]

    l = l.strip()
    if not l: continue #<-- skip empty lines

    kCur = l[1:-1] if (l[0], l[-1]) == ('[', ']') else l #<-- trim [] 
    result.setdefault(kCur, {})

# remove '__no_parent_key__' if empty:
if not result['__no_parent_key__']: del result['__no_parent_key__']

For your sample snippet, result will look like

{ 
  'ThisShouldBeAKey': {'Value1': 'yes', 'Value2': 'no', 'Value3': 'fancywords', 'Value4': 'numbers'},
  'ThisShouldBeAnotherKey': {'Value2': 'true', 'Value3': '333', 'Value5': 'qqq'} 
}

You could also use a reference dictionary (like kwList below) and ast.literal_eval to parse the values a bit further.

kwList = {'true':True, 'false':False, 'null': None, 'none': None}
def try_eval(literalStr:str, kwRef=kwList):
    if literalStr.strip().lower() in kwRef: 
        return kwRef[literalStr.strip().lower()]
    try: return ast.literal_eval(literalStr)
    except: return literalStr

To make use of try_eval, change the if '=' in l:.. block in my suggested solution to

    if '=' in l: 
        k, v = l.split('=', 1) 
        result[kCur][k] = try_eval(v)
        continue

then result will look like

{ 
  'ThisShouldBeAKey': {'Value1': 'yes', 'Value2': 'no', 'Value3': 'fancywords', 'Value4': 'numbers'},
  'ThisShouldBeAnotherKey': {'Value2': True, 'Value3': 333, 'Value5': 'qqq'} 
}

[Note the changes in Value2 and Value3 of ThisShouldBeAnotherKey.]

Driftr95
  • 4,572
  • 2
  • 9
  • 21