Extracting a float from a string

If you don’t know which number format will be matched, you cannot specify the number type to parse anyway.

Beware, that’s not type-stable, you may want to do:

Float32(Meta.parse("-1.234f0"))

I was thinking why doesn’t parse(Float32, “-1.234f04”) call this Meta.parse I didn’t of (or Meta; and why do we want to know about Meta/use that), but the former is type-stable, and the latter isn’t, it depends on the string passed in (can also e.g. parse strings, but then as Symbols). I think it’s intentional that the regular parse doesn’t support “f0” ending, you may not want it if files, unless maybe when you do…

One other difference, in case it matters to you:

:Inf

julia> parse(Float32, "Inf")
Inf32

Same issue with parsing NaN, plus:

julia> Meta.parse("-NaN")  # is Expr, unlike Symbol without the minus:
:(-NaN)

[I didn’t know -NaN is a distinct bit-pattern in IEEE floats until recently, but I think you may never encounter it or be able to parse it, anyone know if it CAN pop up?]

But with that regex, we don’t know what the float type will be anyway.

I mean if you want it type-unstable that’s ok (and I like type-unstable code allowed, Julia more powerful that way), it’s just often you don’t want that. If the point is to just get the value, in a type-stable way, then I guess Float64(Meta.parse("-1.234f0")) is usually better, to not round to lower precision Float32 when you actually get a Float64.

Some how as a kind of a summary, my proposal:

extract_number(_str) = Float64(Meta.parse(match(r"-?\d*\.?\d+([ef][+-]?\d*)?", _str).match))
1 Like

I don’t know how general and efficient it is, but maybe it can give the OP,since he doesn’t want to use regex, a useful hint


strg="time t=0.234_ext.bson"
r=findlast(isdigit, strg)
l=findfirst(isdigit, strg)
sgn=(strg[l-1]=='-')*1
strg[l-sgn:r]


strg="time t=0.234f3_ext.bson"
r=findlast(isdigit, strg)
l=findfirst(isdigit, strg)
sgn=strg[l-1]=='-'
strg[l-sgn:r]

another way without using regex (you can add the handling of any ‘-’ sign)

idx=filter(c-> xor(isdigit(strg[c-1]), isdigit(strg[c+1])), 2:length(strg)-1 )

strg[range(extrema(idx).+(1,-1)...)]

Unfortunatly Float64(Meta.parse("Inf")) does not work :frowning:
Any ideas?

Use the Parsers.jl package:

julia> Parsers.parse(Float64, "-1.234f-4")
-0.0001234

See also the discussion in julia#44910 and julia#5690.

2 Likes

I do not understand your code snippet above.

If I define:

strg = "2.968e-01"
idx=filter(c-> xor(isdigit(strg[c-1]), isdigit(strg[c+1])), 2:length(strg)-1 )
strg[range(extrema(idx).+(1,-1)...)]

The error is:

ERROR: ArgumentError: At least one of `length` or `step` must be specified

Proposal v2:

extract_number(_str) = Parsers.parse(Float64, match(r"-?(Inf|(\d*\.?\d+([ef][+-]?\d*)?))", _str).match)

The error seems related to the function range(...). Unfortunatelly I can’t reproduce.
I take this opportunity to correct and make the snippet more generally valid

julia> strg = "xxx2.968e-01xxx"
"xxx2.968e-01xxx"

julia> idx=filter(c-> xor(isdigit(strg[max(1,c-1)]), isdigit(strg[min(length(strg),c+1)])), 1:length(strg) )
8-element Vector{Int64}:
  3
  6
  8
  9
 10
 11
 12
 13

julia> strg[range(extrema(idx).+(1,-1)...)]
"2.968e-01"

PS
Although it is not the most suitable solution for the case in question, I just wanted to illustrate the use of the XOR function that I have seen usefully used in another context

It is a Julia version issue, on Julia v1.8.2 it works for me.