Parse vector from string

I can parse(Int, „123“) but I cannot find a simple way to parse(Vector{Int}, „[1, 2, 3]”). Is there a “canned” solution?

3 Likes

Broadcast the function.

x = ["1", "2", "3"]
y = parse.(Int64,x)
1 Like

If it is arbitrary julia code you can Meta.parse and eval it it but perhaps using something like JSON would be more suitable here?

If you just need to parse this exact format, splitting on the comma and broadcasting the parse would likely be the most efficient.

1 Like

Not necessarily sure if it is always faster to broadcast and do multiple calls to the parser instead of calling the parser a single time to instead parse a slightly larger string. Might be better sometimes to make single call.

The julia source code parser and the parser that parses types like Ints are two completely different things.

julia> f(x) = parse(Int, x)
f (generic function with 1 method)

julia> @btime f("2")
  46.842 ns (0 allocations: 0 bytes)
2

julia> g(x) = Meta.parse(x)
f (generic function with 1 method)

julia> @btime g("2")
  19.187 μs (7 allocations: 208 bytes)
2

You should only use Meta.parse if you are parsing actual julia source code.

3 Likes

As a simple solution in the past I’ve used:

@assert str[1] == '[' && str[end] == ']'
CSV.read(IOBuffer(str[2:prevind(str, end)]), header=false, transpose=true)
1 Like

Thank you - I was looking as CSC but didn’t see this trick.

For curiosity, if I am given a strIng as Inshowed then how do Inconvert it into an array of strings?

1 Like

Sorry for misunderstanding earlier. Here’s something you can do if you really have to work with the string "[1, 2, 3]"

x = "[1, 2, 3]"
rr = r"([0-9])" # you can alter this to work with numbers with decimal places
matches = [parse(Int64,t.match) for t in eachmatch(rr, x)]

> 3-element Array{Int64,1}:
 1
 2
 3
1 Like

Very interesting, thank you. I’ll need to spend some time understanding this :slight_smile:

Another workaround is to make it a full expression

vec = "[1, 2, 3]"
y = "y = "
t = y * vec
out = eval(parse(t))

Definitely don’t do that. Using eval — and especially eval(parse(…)) — is a major red flag. It doesn’t work the way you expect within functions and it is prone to security issues and surprising bugs.

5 Likes

Hi! I’m a bit of a metaprogramming noob but I’d like to understand a bit better why parsing a string that looks like the definition of a vector could be a bad idea. If you can point me to some relevant documentation I’d be very happy :slight_smile:

See this thread: How to warn new users away from metaprogramming, e.g. my comment on avoiding misuse of metaprogramming: How to warn new users away from metaprogramming - #22 by stevengj

2 Likes

Thanks a lot! Your comment and many other valid points in the thread were quite useful.

The bigger problem than Meta.parse is eval:

Julia’s eval only works at global scope. If you do something like eval(parse("y = [1,2,3]")), it’ll create a global y that’s separate from any local ys you might have inside your function. At best, it’ll be very slow and fragile.

Especially if you’re parsing user input.

julia> eval(Meta.parse("[1,2,3,run(`echo 'rm -rf /'`)]"));
rm -rf /

Oops.

4 Likes

that’s a pretty good example! thanks

awesome solution. works perfectly for me. thanks

a slightly different version but similar to other proposals

vecfromstr(str)=parse.(Int,split(filter(x->isdigit(x)||x==',',  str),','))

this expression gives me the following error

julia> CSV.read(IOBuffer(str[2:prevind(str, end)]), header=false, transpose=true)
ERROR: ArgumentError: provide a valid sink argument, like `using DataFrames; CSV.read(source, DataFrame)`