แปลงสตริง Elixir เป็นจำนวนเต็มหรือลอย


99

ฉันต้องการแปลงสตริงเป็นค่าทศนิยมหรือจำนวนเต็ม ไม่มีวิธีการเช่น

string_to_integer

คำตอบ:


158

ตรวจสอบInteger.parse/1และFloat.parse/1.


37
โปรดทราบว่าสิ่งนี้จะส่งคืนทูเปิล (หากสำเร็จ) ไม่ใช่จำนวนเต็มโดยตรง ถ้าคุณต้องการที่จะทำเช่นนั้นให้ดูที่ @Szymon Jeżตอบด้วยString.to_integer/1

6
มีเหตุผลอะไรที่ต้องใช้Integer.parse/1มากกว่านี้String.to_integer/1หรือไม่?
Ian Vaughan

10
@IanVaughan Integer.parse/1ส่งคืน:errorอะตอมหากไม่สำเร็จ พ่นString.to_integer/1 (FunctionClauseError)
Jonathan Soifer

หากต้องการเพิ่มไปยังจุดที่โจนาธาน, String.to_float("0")โยนข้อผิดพลาดในขณะที่ผลตอบแทนFloat.parse("0") {0.0, ""}สิ่งนี้ทำให้ฉันใช้ฟังก์ชันเดียวเมื่อสตริงของฉันอาจไม่มีจุดทศนิยม
acheroncaptain

53

นอกจากนี้ยังมีInteger.parse/1และFloat.parse/1ฟังก์ชั่นที่JoséปัญหาคุณยังสามารถตรวจสอบและString.to_integer/1String.to_float/1

คำแนะนำ: ดูยังto_atom/1, to_char_list/1, to_existing_atom/1สำหรับการแปลงอื่น ๆ


28

ขอบคุณผู้คนในหน้านี้เพียงแค่ทำให้คำตอบง่ายขึ้นที่นี่:

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

เนื่องจากตรวจสอบความถูกต้องว่าสตริงทั้งหมดถูกแยกวิเคราะห์ (ไม่ใช่แค่คำนำหน้า)


สิ่งนี้จะทำให้เกิดข้อผิดพลาดหาก val ไม่ใช่จำนวนเต็ม ฉันเพิ่มกรณีของผลลัพธ์เพื่อให้แน่ใจว่าการแปลงประสบความสำเร็จ ประโยคที่สองสามารถจับได้โดยทั่วไป: ข้อผิดพลาดหรือสตริงที่สองที่ไม่ว่างเปล่าเนื่องจากคุณไม่สนใจว่าอินพุตเป็น "x3" หรือ "3x"
Sinc

14

มี 4 ฟังก์ชันในการสร้างตัวเลขจากสตริง

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

String.to_integerใช้งานได้ดี แต่String.to_floatยากกว่า:

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

เนื่องจากString.to_floatสามารถจัดการกับโฟลตที่มีรูปแบบดีเท่านั้นเช่น1.0ไม่ใช่1(จำนวนเต็ม) ที่ได้รับการบันทึกไว้ในเอกสารString.to_floatของ

ส่งคืนค่าลอยที่มีการแสดงข้อความเป็นสตริง

สตริงต้องเป็นตัวแทนสตริงของ float รวมถึงจุดทศนิยม ในการแยกวิเคราะห์สตริงที่ไม่มีจุดทศนิยมเป็นทศนิยมควรใช้ Float.parse / 1 มิฉะนั้น ArgumentError จะถูกยกขึ้น

แต่Float.parseส่งคืนทูเปิลของ 2 องค์ประกอบไม่ใช่จำนวนที่คุณต้องการดังนั้นการใส่ลงในไปป์ไลน์จึงไม่ "เจ๋ง":

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]

การใช้elemเพื่อรับองค์ประกอบแรกจากทูเปิลทำให้สั้นและหวานขึ้น:

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

คุณสามารถแปลงเป็น char_list จากนั้นใช้ Erlang to_integer/1หรือto_float/1.

เช่น

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

iex> myInt
23

วิธีใช้ในฟังก์ชั่น? ทางออกที่ดีที่สุดของฉันคือfn q -> {v, _} = Float.parse(q); v endสิ่งที่ฉันไม่ชอบ ฉันต้องการใช้ในEnum.mapเช่นlist |> Enum.map(&String.to_float/1)แต่ string.to_float ใช้กับตัวเลขจำนวนเต็มไม่ได้?
Zhomart

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

1
IMO นี่คือคำตอบที่ดีที่สุด
Marcin Adamczyk

ฉันได้รับข้อผิดพลาดนี้: ฟังก์ชัน ** (UndefinedFunctionError) Decimal.new/1 ไม่ได้กำหนดไว้ (ทศนิยมของโมดูลไม่พร้อมใช้งาน)
Daniel Cukier

4

ปัญหาในการใช้Integer.parse/1คือจะแยกวิเคราะห์ส่วนที่ไม่ใช่ตัวเลขของสตริงตราบเท่าที่มันอยู่ในส่วนท้าย ตัวอย่างเช่น:

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

ในทำนองเดียวกันString.to_integer/1มีผลลัพธ์ดังต่อไปนี้:

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

แต่ให้ตรวจสอบความถูกต้องของสตริงก่อน

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

นิพจน์ทั่วไปอาจง่ายกว่า (เช่น^[0-9]*$) ขึ้นอยู่กับกรณีการใช้งานของคุณ


0

หากคุณต้องการแปลงสตริงเป็นประเภทตัวเลขใด ๆ ที่อยู่ในสตริงและลบอักขระอื่น ๆ ทั้งหมดนี่อาจเป็นการ overkill แต่จะคืนค่า float หากเป็น float หรือ int หากเป็น int หรือ nil หากสตริงไม่มี ประเภทตัวเลข

@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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.