107
votes

I need to convert a string to a floating point value or an integer. There was no method such as,

string_to_integer
8

8 Answers

56
votes

In addition to the Integer.parse/1 and Float.parse/1 functions which José suggested you may also check String.to_integer/1 and String.to_float/1.

Hint: See also to_atom/1,to_char_list/1,to_existing_atom/1for other conversions.

32
votes

Thanks folks on this page, just simplifying an answer here:

{intVal, ""} = Integer.parse(val)

as it validates that the entire string was parsed (not just a prefix).

20
votes

There are 4 functions to create number from string

  • String.to_integer, String.to_float
  • Integer.parse, Float.parse

String.to_integer works nicely but String.to_float is tougher:

iex()> "1 2 3 10 100" |> String.split |> Enum.map(&String.to_integer/1)
[1, 2, 3, 10, 100]

iex()> "1.0 1 3 10 100" |> String.split |> Enum.map(&String.to_float/1)
** (ArgumentError) argument error
    :erlang.binary_to_float("1")
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2

As String.to_float can only handle well-formatted float, e.g: 1.0, not 1 (integer). That was documented in String.to_float's doc

Returns a float whose text representation is string.

string must be the string representation of a float including a decimal point. In order to parse a string without decimal point as a float then Float.parse/1 should be used. Otherwise, an ArgumentError will be raised.

But Float.parse returns a tuple of 2 elements, not the number you want, so put it into pipeline is not "cool":

iex()> "1.0 1 3 10 100" |> String.split \
|> Enum.map(fn n -> {v, _} = Float.parse(n); v end)

[1.0, 1.0, 3.0, 10.0, 100.0]

Using elem to get first element from tuple make it shorter and sweeter:

iex()> "1.0 1 3 10 100" |> String.split \
|> Enum.map(fn n -> Float.parse(n) |> elem(0) end)

[1.0, 1.0, 3.0, 10.0, 100.0]
11
votes

You can convert it to a char_list and then use the Erlang to_integer/1 or to_float/1.

E.g.

iex> {myInt, _} = :string.to_integer(to_char_list("23"))
{23, []}

iex> myInt
23
6
votes
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
4
votes

The problem with using Integer.parse/1 is that is will parse any non-numeric part of the string as long as it is in the tail end. For example:

Integer.parse("01") # {1, ""}
Integer.parse("01.2") # {1, ".2"}
Integer.parse("0-1") # {0, "-1"}
Integer.parse("-01") # {-1, ""}
Integer.parse("x-01") # :error
Integer.parse("0-1x") # {0, "-1x"}

Similarly String.to_integer/1 has the following results:

String.to_integer("01") # 1
String.to_integer("01.2") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("0-1") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("-01") # -1
String.to_integer("x-01") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("0-1x") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")

Instead, validate the string first.

re = Regex.compile!("^[+-]?[0-9]*\.?[0-9]*$")
Regex.match?(re, "01") # true
Regex.match?(re, "01.2") # true
Regex.match?(re, "0-1") # false
Regex.match?(re, "-01") # true
Regex.match?(re, "x-01") # false
Regex.match?(re, "0-1x") # false

The regular expression could be simpler (e.g. ^[0-9]*$) depending on your use case.

2
votes

If you wanted to convert a string to whatever numeric type is within the string & remove all other characters, this is probably overkill, but will return a float if its a float or an int if its an int or nil if the string does not contain an numeric type.

@spec string_to_numeric(binary()) :: float() | number() | nil
def string_to_numeric(val) when is_binary(val), do: _string_to_numeric(Regex.replace(~r{[^\d\.]}, val, ""))
defp _string_to_numeric(val) when is_binary(val), do: _string_to_numeric(Integer.parse(val), val)
defp _string_to_numeric(:error, _val), do: nil
defp _string_to_numeric({num, ""}, _val), do: num
defp _string_to_numeric({num, ".0"}, _val), do: num
defp _string_to_numeric({_num, _str}, val), do: elem(Float.parse(val), 0)