Converter string Elixir em número inteiro ou flutuante

97

Preciso converter uma string em um valor de ponto flutuante ou um inteiro. Não havia nenhum método como,

string_to_integer
Lahiru
fonte

Respostas:

153

Verifique Integer.parse/1e Float.parse/1.

José Valim
fonte
36
Observe que isso retornará uma tupla (se for bem-sucedido) e não o inteiro diretamente. Se você quiser fazer isso, veja @Szymon Jeż responder comString.to_integer/1
6
Existe alguma razão para usar Integer.parse/1mais String.to_integer/1?
Ian Vaughan
10
@IanVaughan Integer.parse/1retorna um :errorátomo se malsucedido. String.to_integer/1joga um (FunctionClauseError).
Jonathan Soifer
52

Além das funções Integer.parse/1e Float.parse/1que José sugeriu, você também pode verificar String.to_integer/1e String.to_float/1.

Dica: Veja também to_atom/1, to_char_list/1, to_existing_atom/1para outras conversões.

Szymon Jeż
fonte
27

Obrigado pessoal nesta página, apenas simplificando uma resposta aqui:

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

uma vez que valida que toda a string foi analisada (não apenas um prefixo).

Roozbeh Zabihollahi
fonte
Isso gerará um erro se o val não for puramente um inteiro. Eu adicionei um caso ao resultado para garantir que a conversão foi bem-sucedida. A segunda cláusula pode ser genérica para catch: error ou uma segunda string não vazia, pois você não se importa muito se a entrada foi "x3" ou "3x".
Sinc
14

Existem 4 funções para criar um número a partir da string

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

String.to_integerfunciona bem, mas String.to_floaté mais difícil:

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

Como String.to_floatsó pode lidar com float bem formatado, por exemplo:, 1.0não 1(inteiro). Isso foi documentado no String.to_floatdoc de

Retorna um float cuja representação de texto é string.

string deve ser a representação de string de um float incluindo um ponto decimal. Para analisar uma string sem ponto decimal como um float, Float.parse / 1 deve ser usado. Caso contrário, um ArgumentError será gerado.

Mas Float.parseretorna uma tupla de 2 elementos, não o número que você deseja, então colocá-lo no pipeline não é "legal":

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]

Usar elempara obter o primeiro elemento da tupla torna-o mais curto e mais doce:

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]
HVNSweeting
fonte
11

Você pode convertê-lo em um char_list e então usar o Erlang to_integer/1ou to_float/1.

Por exemplo

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

iex> myInt
23
Jonas
fonte
Como usar em funções? Minha melhor solução é aquela fn q -> {v, _} = Float.parse(q); v endque eu não gosto. Eu gosto de usar Enum.map, por exemplo, list |> Enum.map(&String.to_float/1)mas string.to_float não funciona para números inteiros?
Zhomart
5
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
kangkyu
fonte
1
IMO esta é a melhor resposta.
Marcin Adamczyk
Recebi este erro: ** (UndefinedFunctionError) function Decimal.new/1 is undefined (o módulo Decimal não está disponível)
Daniel Cukier
4

O problema de usar Integer.parse/1é que ele analisa qualquer parte não numérica da string, desde que esteja no final. Por exemplo:

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"}

Da mesma forma, String.to_integer/1tem os seguintes resultados:

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")

Em vez disso, valide a string primeiro.

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

A expressão regular pode ser mais simples (por exemplo ^[0-9]*$), dependendo do seu caso de uso.

Nibir Bora
fonte
0

Se você quiser converter uma string para qualquer tipo numérico que esteja dentro da string e remover todos os outros caracteres, isso provavelmente é um exagero, mas retornará um float se for um float ou um int se for um int ou nil se a string não contiver um tipo numérico.

@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)
jrichocean
fonte