How to parse an arbitrary parameter string in a python dictionary

I am trying to find the most Pythonic way to take a line containing command line options:

"-t 500 -x -c 3 -d" 

And turn it into a dictionary

 {"-t":"500", "-x":True, "-c":"3", "-d": True} 

UPDATE The line should also contain - long parameters and words with a dash in the middle:

 "-t 500 -x -c 3 -d --long-option 456 -testing weird-behaviour" 

Before suggesting that I look into the OptionParse module, keep in mind that I don’t know what the actual parameters are or something like that, I’m just trying to put a string in a dictionary to allow it to change based on another options dictionary.

The approach I'm considering is to use split () to get the items in the list, and then go through the list and look for items starting with a dash β€œ-”, and use them as a key, and then somehow get to the next item in the list for value. I have problems with parameters that do not have values. I was thinking of doing something like:

 for i in range(0, len(opt_list)): if opt_list[i][0] == "-": if len(opt_list) > i+1 and not opt_list[i+1][0] == "-": opt_dict[opt_list[i]] = opt_list[i+1] else: opt_dict[opt_list[i]] = True 

But it seems to me that I am programming in C not Python when I do this ...

+6
source share
7 answers

To properly handle spaces inside quotes, you can use shlex.split() :

 import shlex cmdln_args = ('-t 500 -x -c 3 -d --long-option 456 '       '-testing "weird -behaviour" -m "--inside"') args = shlex.split(cmdln_args) options = {k: True if v.startswith('-') else v      for k,v in zip(args, args[1:]+["--"]) if k.startswith('-')} from pprint import pprint pprint(options) 

Exit

 {'--inside': True, '--long-option': '456', '-c': '3', '-d': True, '-m': True, '-t': '500', '-testing': 'weird -behaviour', '-x': True} 
+5
source

You can use regular expressions, for example:

 import re args = "-t 500 -x -c 3 -d --long-option 456 -testing weird-behaviour" matches = re.findall(r'(--?[\w-]+)(.*?)(?= -|$)', args) result = {} for match in matches: result[match[0]] = True if not match[1] else match[1].strip() print result 

and the result is

 { '-d': True, '-c': '3', '-t': '500', '--long-option': '456', '-x': True, '-testing': 'weird-behaviour' } 

Regular Expression Distribution:

(--?[\w-]+)(.*?)(?= -|$)

  • (--?[\w-]+) matches any character or word (dash permitted in the word) that begins with a "-" or "-".
  • (.*?) matches any character 0 or more times in non-living or minimal mode using a question mark.
  • (?= -|$) is a positive result. He checks that what we are looking for is followed by a β€œ-” or the end of a line, but he does not include it in the match.

Note the use of parentheses in this regex. They are used to create groups, so when we call findall , it breaks them into tuples.

+3
source

Argument Analysis for people - https://github.com/kennethreitz/args

+2
source

I can't say the most Pythonic , but here is a 1-liner:

 opt_list = "-t 500 -x -c 3 -d" dict((e if len(e) >1 else (e[0],True) for e in (elem.split() for elem in ('-'+d for d in opt_list.split('-') if d)))) 

>>>{'-t': '500', '-x': True, '-c': '3', '-d': True}

[Edit: As Matthias pointed out, this will not work for values ​​with the '-' symbol in them]

... however, in general, I do not think that the OP answer can be unambiguously resolved if you allowed '-' in the option values.

consider these simple options:

"- a -b"

It:

  • {'- a': '-b'},
  • {'a -': True, '-b': True}

???

+1
source
 >>> results = "-t 500 -x -c 3 -d".split() >>> rd = {} >>> while i < len(results): ... if results[i].startswith("-"): ... rd[results[i]]=True ... try: ... if not results[i+1].startswith("-"): ... rd[results[i]] = results[i+1] ... except IndexError: pass ... i+=1 ... >>> rd {'-t': '500', '-x': True, '-c': '3', '-d': True} 

but pretty similar to what you have.

0
source

The more complex problem it starts with is my first attempt. It simply iterates over the arguments and checks to see if they begin with - . If this is the case, but the next argument is not, then these two elements are added to the dictorial, otherwise the current argument and True are added. try is required if the last item in the argument list starts with - .

 args = "-t 500 -x -c 3 -d".split() d = {} for i, item in enumerate(args): if item.startswith('-'): try: if args[i+1].startswith('-'): d[item] = True else: d[item] = args[i+1] except IndexError: d[item] = True print d # prints {'-t': '500', '-x': True, '-c': '3', '-d': True} 

Edit: An alternative solution, inspired by the Gerrat section on - , - as follows:

 args = "-t 500 -x -c 3 -d".split('-') d = {} for arg in args: if arg: try: k, v = arg.split() except ValueError: k, v = arg.strip(), True d[k] = v 

However, as Matthias points out, this may not work if the parameters and values ​​have - inside them.

0
source
 import re myDictionnary = {} strPattern1 = "-[0-9a-z ]*" strPattern2 = "-([0-9a-z]+) *(.*)" strToParse = "-t 500 -x -c 3 -d" listKeyValues = re.findall(strPattern1, strToParse) for kv in listKeyValues: match = re.search(strPattern2, kv) key = match.group(1) value = match.group(2) if len(value) > 0: myDictionnary[key] = value else: myDictionnary[key] = True 
0
source

Source: https://habr.com/ru/post/923141/


All Articles