Saya memiliki berbagai string, beberapa seperti "45", beberapa seperti "45px". Bagaimana cara saya mengonversi keduanya ke nomor 45?
"9"
ke 9
, ini adalah hal terbaik yang bekerja untuk saya: (Integer. "9")
.
Saya memiliki berbagai string, beberapa seperti "45", beberapa seperti "45px". Bagaimana cara saya mengonversi keduanya ke nomor 45?
"9"
ke 9
, ini adalah hal terbaik yang bekerja untuk saya: (Integer. "9")
.
Jawaban:
Ini akan bekerja pada 10px
ataupx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
itu hanya akan menguraikan digit kontinu pertama
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Saya lebih suka jawaban snrobot. Menggunakan metode Java lebih sederhana dan lebih kuat daripada menggunakan read-string untuk kasus penggunaan sederhana ini. Saya memang membuat beberapa perubahan kecil. Karena penulis tidak mengesampingkan angka negatif, saya menyesuaikannya untuk memungkinkan angka negatif. Saya juga membuatnya sehingga membutuhkan nomor untuk memulai di awal string.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Selain itu saya menemukan bahwa Integer / parseInt mem-parsing sebagai desimal ketika tidak ada radix yang diberikan, bahkan jika ada nol di depannya.
Pertama, untuk mengurai hanya integer (karena ini adalah hit di google dan itu informasi latar belakang yang bagus):
Anda dapat menggunakan pembaca :
(read-string "9") ; => 9
Anda dapat memeriksa apakah itu nomor setelah dibaca:
(defn str->int [str] (if (number? (read-string str))))
Saya tidak yakin apakah input pengguna dapat dipercaya oleh pembaca clojure sehingga Anda dapat memeriksa sebelum membacanya juga:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Saya pikir saya lebih suka solusi terakhir.
Dan sekarang, untuk pertanyaan spesifik Anda. Untuk mem-parsing sesuatu yang dimulai dengan integer, seperti 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
harus menjadi when
karena tidak ada lagi blok di fns Anda.
read-string
menafsirkannya sebagai oktal: (read-string "08")
melempar pengecualian. Integer/valueOf
memperlakukannya sebagai desimal: (Integer/valueOf "08")
dievaluasi menjadi 8.
read-string
melempar pengecualian jika Anda memberinya string kosong atau sesuatu seperti "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, daripada konstruktor Integer. Kelas Integer menyimpan nilai antara -128 dan 127 untuk meminimalkan pembuatan objek. Integer Javadoc menjelaskan ini seperti halnya posting ini: stackoverflow.com/a/2974852/871012
Ini bekerja sebagai gantinya bagi saya, jauh lebih lurus ke depan.
(baca-string "123")
=> 123
read-string
dapat mengeksekusi kode per dokumen: clojuredocs.org/clojure.core/read-string
AFAIK tidak ada solusi standar untuk masalah Anda. Saya pikir sesuatu seperti yang berikut ini, yang menggunakan clojure.contrib.str-utils2/replace
, akan membantu:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... dan itu juga tidak menggunakan clojure.string/replace
fungsi bawaan.
Ini tidak sempurna, tapi ada sesuatu dengan filter
, Character/isDigit
dan Integer/parseInt
. Ini tidak akan berfungsi untuk angka floating point dan gagal jika tidak ada digit di input, jadi Anda mungkin harus membersihkannya. Saya harap ada cara yang lebih baik untuk melakukan ini yang tidak melibatkan begitu banyak Jawa.
user=> (defn strToInt [x] (Integer/parseInt (apply str (filter #(Character/isDigit %) x))))
#'user/strToInt
user=> (strToInt "45px")
45
user=> (strToInt "45")
45
user=> (strToInt "a")
java.lang.NumberFormatException: For input string: "" (NO_SOURCE_FILE:0)
Saya mungkin akan menambahkan beberapa hal ke persyaratan:
Mungkin sesuatu seperti:
(defn parse-int [v]
(try
(Integer/parseInt (re-find #"^\d+" (.toString v)))
(catch NumberFormatException e 0)))
(parse-int "lkjhasd")
; => 0
(parse-int (java.awt.Color. 4 5 6))
; => 0
(parse-int "a5v")
; => 0
(parse-int "50px")
; => 50
dan mungkin poin bonus untuk menjadikan ini metode multi yang memungkinkan untuk bawaan yang disediakan pengguna selain 0.
Memperluas jawaban snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Versi ini mengembalikan nihil jika tidak ada digit di input, daripada meningkatkan pengecualian.
Pertanyaan saya adalah apakah boleh menyingkat nama menjadi "str-> int", atau apakah hal-hal seperti ini harus selalu ditentukan secara penuh.
Juga menggunakan (re-seq)
fungsi dapat memperluas nilai kembali ke string yang berisi semua angka yang ada di string input agar:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
Pertanyaannya adalah tentang mengurai string menjadi angka.
(number? 0.5)
;;=> true
Jadi dari desimal di atas harus diurai juga.
Mungkin tidak tepat menjawab pertanyaan sekarang, tetapi untuk penggunaan umum saya pikir Anda ingin menjadi ketat tentang apakah itu angka atau tidak (jadi "px" tidak diizinkan) dan biarkan penelepon menangani non-angka dengan mengembalikan nihil:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
Dan jika Floats bermasalah untuk domain Anda, bukan Float/parseFloat
put bigdec
atau sesuatu yang lain.
Untuk orang lain yang ingin mengurai String literal yang lebih normal menjadi angka, yaitu string yang tidak memiliki karakter non numerik lainnya. Ini adalah dua pendekatan terbaik:
Menggunakan Java interop:
(Long/parseLong "333")
(Float/parseFloat "333.33")
(Double/parseDouble "333.3333333333332")
(Integer/parseInt "-333")
(Integer/parseUnsignedInt "333")
(BigInteger. "3333333333333333333333333332")
(BigDecimal. "3.3333333333333333333333333332")
(Short/parseShort "400")
(Byte/parseByte "120")
Ini memungkinkan Anda mengontrol dengan tepat jenis yang ingin Anda parsing nomornya, ketika itu penting untuk use case Anda.
Menggunakan pembaca Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Tidak seperti menggunakan read-string
dari clojure.core
yang tidak aman untuk digunakan pada input yang tidak dipercaya, edn/read-string
aman untuk dijalankan pada input yang tidak dipercaya seperti input pengguna.
Ini seringkali lebih nyaman daripada interop Java jika Anda tidak perlu memiliki kontrol khusus dari jenisnya. Itu dapat menguraikan sejumlah literal yang dapat diurai Clojure seperti:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Daftar lengkap di sini: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Untuk kasus sederhana, Anda bisa menggunakan regex untuk menarik string digit pertama seperti yang disebutkan di atas.
Jika Anda memiliki situasi yang lebih rumit, Anda mungkin ingin menggunakan perpustakaan InstaParse:
(ns tst.parse.demo
(:use tupelo.test)
(:require
[clojure.string :as str]
[instaparse.core :as insta]
[tupelo.core :as t] ))
(t/refer-tupelo)
(dotest
(let [abnf-src "
size-val = int / int-px
int = digits ; ex '123'
int-px = digits <'px'> ; ex '123px'
<digits> = 1*digit ; 1 or more digits
<digit> = %x30-39 ; 0-9
"
tx-map {:int (fn fn-int [& args]
[:int (Integer/parseInt (str/join args))])
:int-px (fn fn-int-px [& args]
[:int-px (Integer/parseInt (str/join args))])
:size-val identity
}
parser (insta/parser abnf-src :input-format :abnf)
instaparse-failure? (fn [arg] (= (class arg) instaparse.gll.Failure))
parse-and-transform (fn [text]
(let [result (insta/transform tx-map
(parser text))]
(if (instaparse-failure? result)
(throw (IllegalArgumentException. (str result)))
result))) ]
(is= [:int 123] (parse-and-transform "123"))
(is= [:int-px 123] (parse-and-transform "123px"))
(throws? (parse-and-transform "123xyz"))))
(t/refer-tupelo)
alih-alih membuat pengguna melakukannya (:require [tupelo.core :refer :all])
?
refer-tupelo
dimodelkan setelah refer-clojure
, dalam hal itu tidak termasuk segala cara (:require [tupelo.core :refer :all])
.