Convierta la cadena Elixir en entero o flotante

97

Necesito convertir una cadena en un valor de punto flotante o un número entero. No existía un método como,

string_to_integer
Lahiru
fuente

Respuestas:

153

Compruebe Integer.parse/1y Float.parse/1.

José Valim
fuente
36
Tenga en cuenta que esto devolverá una tupla (si tiene éxito) y no el número entero directamente. Si quieres hacer eso, mira @Szymon Jeż responder conString.to_integer/1
6
¿Hay alguna razón para usar Integer.parse/1más String.to_integer/1?
Ian Vaughan
10
@IanVaughan Integer.parse/1devuelve un :errorátomo si no tiene éxito. String.to_integer/1lanza un (FunctionClauseError).
Jonathan Soifer
52

Además de las funciones Integer.parse/1y Float.parse/1que sugirió José, también puede verificar String.to_integer/1y String.to_float/1.

Sugerencia: Véase también to_atom/1, to_char_list/1, to_existing_atom/1para otras conversiones.

Szymon Jeż
fuente
27

Gracias a la gente de esta página, simplemente simplificando una respuesta aquí:

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

ya que valida que se analizó toda la cadena (no solo un prefijo).

Roozbeh Zabihollahi
fuente
Esto arrojará un error si val no es puramente un entero. Agregué un caso en el resultado para asegurarme de que la conversión se realizó correctamente. La segunda cláusula puede ser genérica para capturar: error o una segunda cadena no vacía, ya que no le importa mucho si la entrada fue "x3" o "3x".
Sinc
14

Hay 4 funciones para crear un número a partir de una cadena

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

String.to_integerfunciona bien pero String.to_floates más 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_floatsolo puede manejar un flotante bien formateado, por ejemplo:, 1.0no 1(entero). Eso fue documentado en String.to_floatel documento de

Devuelve un flotante cuya representación de texto es una cadena.

cadena debe ser la representación de cadena de un flotante que incluya un punto decimal. Para analizar una cadena sin punto decimal como flotante, se debe usar Float.parse / 1. De lo contrario, se generará un ArgumentError.

Pero Float.parsedevuelve una tupla de 2 elementos, no el número que desea, por lo que ponerlo en canalización no es "genial":

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 obtener el primer elemento de la tupla lo hace más corto y dulce:

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
fuente
11

Puede convertirlo a char_list y luego usar Erlang to_integer/1o to_float/1.

P.ej

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

iex> myInt
23
Jonas
fuente
¿Cómo usarlo en funciones? Mi mejor solución es la fn q -> {v, _} = Float.parse(q); v endque no me gusta. Me gusta usarlo Enum.map, por ejemplo, list |> Enum.map(&String.to_float/1)pero ¿string.to_float no funciona para números enteros?
Zhomart
5
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
kangkyu
fuente
1
En mi opinión, esta es la mejor respuesta.
Marcin Adamczyk
Recibí este error: ** (UndefinedFunctionError) La función Decimal.new/1 no está definida (el módulo Decimal no está disponible)
Daniel Cukier
4

El problema con el uso Integer.parse/1es que analizará cualquier parte no numérica de la cadena siempre que esté al final. Por ejemplo:

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

Del mismo modo String.to_integer/1tiene los siguientes 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")

En su lugar, valide la cadena primero.

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

La expresión regular podría ser más simple (por ejemplo ^[0-9]*$) dependiendo de su caso de uso.

Nibir Bora
fuente
0

Si desea convertir una cadena a cualquier tipo numérico que esté dentro de la cadena y eliminar todos los demás caracteres, esto probablemente sea excesivo, pero devolverá un flotante si es un flotante o un int si es un int o nil si la cadena no contiene un 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
fuente