Muszę przekonwertować ciąg na wartość zmiennoprzecinkową lub liczbę całkowitą. Nie było takiej metody jak:
string_to_integer
Odpowiedzi:
Sprawdź Integer.parse/1
i Float.parse/1
.
Integer.parse/1
over String.to_integer/1
?
Integer.parse/1
zwraca :error
atom, jeśli się nie powiedzie. String.to_integer/1
rzuca (FunctionClauseError)
.
Oprócz funkcji Integer.parse/1
i, Float.parse/1
które zasugerował José, możesz również sprawdzić String.to_integer/1
i String.to_float/1
.
Wskazówka: Patrz również to_atom/1
, to_char_list/1
, to_existing_atom/1
dla innych konwersji.
Dzięki ludziom na tej stronie, upraszczam odpowiedź tutaj:
{intVal, ""} = Integer.parse(val)
ponieważ sprawdza, czy przeanalizowano cały ciąg (a nie tylko przedrostek).
Istnieją 4 funkcje do tworzenia liczby z łańcucha
String.to_integer
działa ładnie, ale String.to_float
jest twardszy:
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
Jak String.to_float
obsługuje tylko dobrze sformatowaną liczbę zmiennoprzecinkową, np .: 1.0
, not 1
(integer). To zostało udokumentowane w dokumencie String.to_float
's
Zwraca liczbę zmiennoprzecinkową, której reprezentacją tekstową jest łańcuch.
ciąg musi być ciągiem reprezentującym wartość zmiennoprzecinkową, w tym kropką dziesiętną. Aby przeanalizować ciąg bez kropki dziesiętnej jako zmiennoprzecinkowy, należy użyć Float.parse / 1. W przeciwnym razie zostanie zgłoszony ArgumentError.
Ale Float.parse
zwraca krotkę z 2 elementów, a nie żądaną liczbę, więc umieszczenie jej w potoku nie jest „fajne”:
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]
Użycie elem
do pobrania pierwszego elementu z krotki sprawi, że będzie on krótszy i słodszy:
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]
Możesz przekonwertować go na char_list, a następnie użyć Erlang to_integer/1
lub to_float/1
.
Na przykład
iex> {myInt, _} = :string.to_integer(to_char_list("23"))
{23, []}
iex> myInt
23
fn q -> {v, _} = Float.parse(q); v end
co mi się nie podoba. Lubię go używać Enum.map
, np. list |> Enum.map(&String.to_float/1)
Ale string.to_float nie działa dla liczb całkowitych?
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
Problem z użyciem Integer.parse/1
polega na tym, że sparsuje każdą nieliczbową część łańcucha, o ile znajduje się na końcu. Na przykład:
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"}
Podobnie String.to_integer/1
ma następujące wyniki:
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")
Zamiast tego najpierw sprawdź poprawność ciągu.
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
Wyrażenie regularne może być prostsze (np. ^[0-9]*$
) W zależności od przypadku użycia.
Jeśli chcesz przekonwertować ciąg na dowolny typ liczbowy w ciągu i usunąć wszystkie inne znaki, prawdopodobnie jest to przesada, ale zwróci wartość typu float, jeśli jest to float lub int, jeśli jest to int lub nil, jeśli łańcuch nie zawiera typ liczbowy.
@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)
String.to_integer/1