5

I would like to convert a String like:

s = "[2-1,2,3]"

into a list, like:

a = [1,2,3]

I tried it with json:

s = "[2-1,2,3]"
a = json.loads(s)

but it can't handle 2-1. Is there an easy method to convert strings into any kind of datatype?

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
M Tomb
  • 53
  • 5
  • 4
    @DanielHao "Try `eval`" is bad advice if you don't caveat it with everything that can go wrong with eval. https://stackoverflow.com/q/1832940/843953 – Pranav Hosangadi Jul 01 '22 at 19:13
  • 4
    `eval` is not necessarily bad, if the input string is under your own control. This is actually pretty difficult to do _without_ `eval`. – wim Jul 01 '22 at 19:15
  • 1
    It would be even better if someone knows a way to convert the following string into an array: s = "[A-1,2,3]" ,where A is a variable – M Tomb Jul 01 '22 at 19:16
  • In this case, the usual alternative (`ast.literal_eval`) doesn't work. I'm not sure why or how to fix that without actually using `eval`. Does that string come from the user or an external source that can be tampered with? – Pranav Hosangadi Jul 01 '22 at 19:17
  • @all, Thank you very much for the advice. The string comes from the user – M Tomb Jul 01 '22 at 19:19

2 Answers2

3

Yes. And as much as it pains me to say it, eval is your friend here, as even ast.literal_eval cannot parse this.

  • Please read this first: eval: bad practice?, and please ensure you have complete control over the expressions being evaluated.
  • To help lessen the expressions being evaluated, I've wrapped this solution in regex, to extract only numbers and (in this case) the minus sign.
  • Obviously, this might need tweaking for your specific use case, this this should give you a boiler-plate (or at least an idea) from which to start.

Example code:

import re

s = "[2-1,2,3]"
rexp = re.compile('[\d-]+')

out = []
for exp in rexp.findall(s):
    out.append(eval(exp))

Or, if you prefer a one-liner:

out = [eval(exp) for exp in rexp.findall(s)]

Output:

[1, 2, 3]
S3DEV
  • 8,768
  • 3
  • 31
  • 42
0

This is a common problem to tackle while writing compilers. Usually this comes under lexing. A parser would usually have a list of tokens, watch for the tokens and then pass it to a parser and then the compiler.

Your problem cannot be completely solved with a lexer though since you also require the 2-1 to evaluate to 1. In this case, I would suggest using eval like @Daniel Hao suggested since it is a simple and clean way of achieving your goal. Remember about the caveats(both security and otherwise) while using it though. (especially, in production)

If you are interested in the parsing though, check this out:

  1. https://craftinginterpreters.com/contents.html
  2. https://tomassetti.me/parsing-in-python/
Naitik Mundra
  • 418
  • 3
  • 14