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?
CodePudding user response:
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]
CodePudding user response:
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:
CodePudding user response:
You could use the JSON.parse
method after converting any expressions to strings. Then you can use the Array#map
method to evaluate the expressions.
const s = "[2-1,2,3]",
output = JSON.parse(s.replace(/(\d [\-\ ]\d )/g, '\"$1\"'))
.map(n => Function(`return (${n})`)());
console.log( output );
CodePudding user response:
Others already mentioned that how/why eval
can be dangerous, BUT If your string contains literals(like here - list of integers) that can be understood by the Parser, you can restrict eval
and safely use it here. If it contains other things you're gonna get exception(see the note-1 below). I passed both global and local dictionary to eval
:
s = "[2-1,2,3]"
lst = eval(s, {"__builtins__": None}, {})
print(lst) # [1, 2, 3]
From documentation:
If the globals dictionary is present and does not contain a value for the key
__builtins__
, a reference to the dictionary of the built-in module builtins is inserted under that key before expression is parsed. That way you can control what builtins are available to the executed code by inserting your own__builtins__
dictionary into globals before passing it to eval().
In comment you mentioned:
It would be even better if someone knows a way to convert the following string into an array:
s = "[A-1,2,3]"
,whereA
is a variable
This can also be achieved by passing A
to the local namespace.
s = "[A,2,3]"
lst = eval(s, {"__builtins__": None}, {"A": 5})
print(lst) # [5, 2, 3]
note-1: Probably TypeError
- since the local is empty, It tries to find it in the global namespace, it's empty too, so it looks at builtins namespace, we specifically set it to be None
and None
is not subscriptable.