Chuyển đổi chuỗi Elixir thành số nguyên hoặc float


97

Tôi cần chuyển đổi một chuỗi thành giá trị dấu phẩy động hoặc số nguyên. Không có phương pháp nào như,

string_to_integer

Câu trả lời:


153

36
Lưu ý rằng điều này sẽ trả về một tuple (nếu thành công) chứ không phải trực tiếp số nguyên. Nếu bạn muốn làm điều đó, xem @Szymon Jez câu trả lời vớiString.to_integer/1

6
Có lý do gì để sử dụng Integer.parse/1hơn String.to_integer/1không?
Ian Vaughan

10
@IanVaughan Integer.parse/1trả về một :errornguyên tử nếu không thành công. String.to_integer/1ném a (FunctionClauseError).
Jonathan Soifer

52

Ngoài các chức năng Integer.parse/1Float.parse/1mà José đã đề xuất, bạn cũng có thể kiểm tra String.to_integer/1String.to_float/1.

Gợi ý: Xem thêm to_atom/1, to_char_list/1, to_existing_atom/1cho chuyển đổi khác.


27

Cảm ơn mọi người trên trang này, chỉ cần đơn giản hóa một câu trả lời ở đây:

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

vì nó xác nhận rằng toàn bộ chuỗi đã được phân tích cú pháp (không chỉ là tiền tố).


Điều này sẽ gây ra lỗi nếu val không hoàn toàn là một số nguyên. Tôi đã thêm một trường hợp vào kết quả để đảm bảo chuyển đổi thành công. Mệnh đề thứ hai có thể chung chung để bắt: lỗi hoặc một chuỗi thứ hai không rỗng vì bạn không quan tâm lắm liệu đầu vào là "x3" hay "3x".
Sinc

14

Có 4 hàm để tạo số từ chuỗi

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

String.to_integerhoạt động tốt nhưng String.to_floatkhó hơn:

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

Như String.to_floatchỉ có thể xử lý float được định dạng tốt, ví dụ:, 1.0not 1(số nguyên). Điều đó đã được ghi lại trong tài liệu String.to_floatcủa

Trả về một float có biểu diễn văn bản là chuỗi.

chuỗi phải là biểu diễn chuỗi của một số thực bao gồm dấu thập phân. Để phân tích cú pháp một chuỗi không có dấu thập phân dưới dạng float thì nên sử dụng Float.parse / 1. Nếu không, ArgumentError sẽ xuất hiện.

Nhưng Float.parsetrả về một bộ gồm 2 phần tử, không phải là số bạn muốn, vì vậy việc đặt nó vào đường ống không phải là "mát mẻ":

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]

Sử dụng elemđể lấy phần tử đầu tiên từ tuple làm cho nó ngắn hơn và ngọt ngào hơn:

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

Bạn có thể chuyển nó thành char_list và sau đó sử dụng Erlang to_integer/1hoặc to_float/1.

Ví dụ

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

iex> myInt
23

Làm thế nào để sử dụng nó trong các chức năng? Giải pháp tốt nhất của tôi là giải pháp fn q -> {v, _} = Float.parse(q); v endmà tôi không thích. Tôi thích sử dụng nó trong Enum.map, ví dụ: list |> Enum.map(&String.to_float/1)nhưng string.to_float không hoạt động với các số nguyên?
Zhomart

5
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float

1
IMO đây là câu trả lời tốt nhất.
Marcin Adamczyk

Tôi gặp lỗi này: Hàm ** (UndefinedFunctionError) Decimal.new/1 là không xác định (mô-đun Decimal không khả dụng)
Daniel Cukier

4

Vấn đề với việc sử dụng Integer.parse/1là sẽ phân tích cú pháp bất kỳ phần không phải số nào của chuỗi miễn là nó nằm ở phần cuối đuôi. Ví dụ:

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

Tương tự String.to_integer/1có kết quả sau:

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

Thay vào đó, hãy xác thực chuỗi trước.

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

Biểu thức chính quy có thể đơn giản hơn (ví dụ ^[0-9]*$) tùy thuộc vào trường hợp sử dụng của bạn.


0

Nếu bạn muốn chuyển đổi một chuỗi thành bất kỳ kiểu số nào trong chuỗi và xóa tất cả các ký tự khác, điều này có thể là quá mức cần thiết, nhưng sẽ trả về một số float nếu nó là float hoặc int nếu là int hoặc nil nếu chuỗi không chứa một kiểu số.

@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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.