Konvertieren Sie die Elixir-Zeichenfolge in eine Ganzzahl oder einen Gleitkommawert

97

Ich muss eine Zeichenfolge in einen Gleitkommawert oder eine Ganzzahl konvertieren. Es gab keine Methode wie:

string_to_integer
Lahiru
quelle

Antworten:

153

Überprüfen Sie Integer.parse/1und Float.parse/1.

José Valim
quelle
36
Beachten Sie, dass dies ein Tupel (falls erfolgreich) und nicht die Ganzzahl direkt zurückgibt. Wenn Sie das tun möchten, sehen Sie @Szymon Jeż Antwort mitString.to_integer/1
6
Gibt es einen Grund zu verwenden , Integer.parse/1über String.to_integer/1?
Ian Vaughan
10
@IanVaughan Integer.parse/1gibt ein :errorAtom zurück, wenn dies nicht erfolgreich ist. String.to_integer/1wirft a (FunctionClauseError).
Jonathan Soifer
52

Zusätzlich zu den Integer.parse/1und Float.parse/1Funktionen, die José vorgeschlagen hat, können Sie auch String.to_integer/1und überprüfen String.to_float/1.

Hinweis: Siehe auch to_atom/1, to_char_list/1, to_existing_atom/1für andere Konvertierungen.

Szymon Jeż
quelle
27

Vielen Dank Leute auf dieser Seite, die hier nur eine Antwort vereinfachen:

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

da es bestätigt, dass die gesamte Zeichenfolge analysiert wurde (nicht nur ein Präfix).

Roozbeh Zabihollahi
quelle
Dies löst einen Fehler aus, wenn der Wert keine reine Ganzzahl ist. Ich habe dem Ergebnis einen Fall hinzugefügt, um sicherzustellen, dass die Konvertierung erfolgreich war. Die zweite Klausel kann generisch sein, um zu fangen: Fehler oder eine nicht leere zweite Zeichenfolge, da es Ihnen egal ist, ob die Eingabe "x3" oder "3x" war.
Seit dem
14

Es gibt 4 Funktionen zum Erstellen einer Nummer aus einer Zeichenfolge

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

String.to_integerfunktioniert gut, String.to_floatist aber härter:

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

Da String.to_floatkann nur gut formatiertes Float verarbeitet werden, zB: 1.0nicht 1(Integer). Das wurde in String.to_float's doc dokumentiert

Gibt einen Float zurück, dessen Textdarstellung eine Zeichenfolge ist.

Zeichenfolge muss die Zeichenfolgendarstellung eines Gleitkommas einschließlich eines Dezimalpunkts sein. Um eine Zeichenfolge ohne Dezimalpunkt als Gleitkomma zu analysieren, sollte Float.parse / 1 verwendet werden. Andernfalls wird ein ArgumentError ausgelöst.

Aber Float.parsegibt ein Tupel von zwei Elementen, nicht die Zahl , die Sie wollen, es so in Pipeline setzen ist nicht „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]

Verwenden Sie elem, um das erste Element aus dem Tupel zu erhalten, und machen Sie es kürzer und süßer:

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

Sie können es in eine char_list konvertieren und dann Erlang to_integer/1oder verwenden to_float/1.

Z.B

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

iex> myInt
23
Jonas
quelle
Wie benutzt man es in Funktionen? Meine beste Lösung ist fn q -> {v, _} = Float.parse(q); v enddie, die ich nicht mag. Ich benutze es gerne in Enum.map, zB list |> Enum.map(&String.to_float/1)aber string.to_float funktioniert nicht für Ganzzahlen?
Zhomart
5
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
Kangkyu
quelle
1
IMO das ist die beste Antwort.
Marcin Adamczyk
Ich habe diesen Fehler erhalten: ** (UndefinedFunctionError) -Funktion Decimal.new/1 ist undefiniert (Modul Decimal ist nicht verfügbar)
Daniel Cukier
4

Das Problem bei der Verwendung Integer.parse/1besteht darin, dass alle nicht numerischen Teile der Zeichenfolge analysiert werden, solange sie sich am hinteren Ende befinden. Beispielsweise:

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

Ebenso String.to_integer/1hat die folgenden Ergebnisse:

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

Überprüfen Sie stattdessen zuerst die Zeichenfolge.

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

Der reguläre Ausdruck kann ^[0-9]*$je nach Anwendungsfall einfacher sein (z. B. ).

Nibir Bora
quelle
0

Wenn Sie eine Zeichenfolge in einen beliebigen numerischen Typ innerhalb der Zeichenfolge konvertieren und alle anderen Zeichen entfernen möchten, ist dies wahrscheinlich ein Overkill, gibt jedoch einen Gleitkommawert zurück, wenn es sich um einen Gleitkommawert handelt, oder einen Int, wenn es sich um einen Int handelt, oder einen Nullwert, wenn der String nicht enthält ein numerischer Typ.

@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
quelle