Tôi có nhiều chuỗi khác nhau, một số như "45", một số như "45px". Làm thế nào để tôi chuyển đổi cả hai thứ này sang số 45?
"9"
thành 9
, đây là điều tốt nhất phù hợp với tôi : (Integer. "9")
.
Tôi có nhiều chuỗi khác nhau, một số như "45", một số như "45px". Làm thế nào để tôi chuyển đổi cả hai thứ này sang số 45?
"9"
thành 9
, đây là điều tốt nhất phù hợp với tôi : (Integer. "9")
.
Câu trả lời:
Điều này sẽ làm việc trên 10px
hoặcpx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
nó sẽ phân tích các chữ số liên tục đầu tiên như vậy
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Tôi thích câu trả lời của snrobot hơn. Sử dụng phương thức Java đơn giản và mạnh mẽ hơn so với sử dụng chuỗi đọc cho trường hợp sử dụng đơn giản này. Tôi đã thực hiện một vài thay đổi nhỏ. Vì tác giả không loại trừ số âm, tôi đã điều chỉnh nó để cho phép số âm. Tôi cũng đã làm cho nó để nó yêu cầu số bắt đầu ở đầu chuỗi.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Ngoài ra, tôi thấy rằng Integer / parseInt phân tích dưới dạng thập phân khi không có cơ số nào được đưa ra, ngay cả khi có các số 0 đứng đầu.
Đầu tiên, để phân tích chỉ một số nguyên (vì đây là một điểm nhấn trên google và đó là thông tin cơ bản tốt):
Bạn có thể sử dụng trình đọc :
(read-string "9") ; => 9
Bạn có thể kiểm tra xem đó có phải là số sau khi đọc không:
(defn str->int [str] (if (number? (read-string str))))
Tôi không chắc liệu đầu vào của người dùng có thể được người đọc clojure tin cậy hay không để bạn có thể kiểm tra trước khi đọc.
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Tôi nghĩ rằng tôi thích giải pháp cuối cùng.
Và bây giờ, câu hỏi cụ thể của bạn. Để phân tích một cái gì đó bắt đầu bằng một số nguyên, như 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
nên là một when
vì không có khối nào khác trong fns của bạn.
read-string
giải thích chúng là bát phân: (read-string "08")
ném một ngoại lệ. Integer/valueOf
coi chúng là số thập phân: (Integer/valueOf "08")
ước tính đến 8.
read-string
sẽ ném một ngoại lệ nếu bạn cung cấp cho nó một chuỗi rỗng hoặc đại loại như "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, thay vì hàm tạo Integer. Lớp Integer lưu trữ các giá trị trong khoảng từ -128 đến 127 để giảm thiểu việc tạo đối tượng. Integer Javadoc mô tả điều này giống như bài đăng này: stackoverflow.com/a/2974852/871012
Điều này làm việc thay thế cho tôi, thẳng hơn nhiều.
(chuỗi đọc "123")
=> 123
read-string
có thể thực thi mã theo các tài liệu: clojuredocs.org/clojure.core/read-opes
AFAIK không có giải pháp chuẩn cho vấn đề của bạn. Tôi nghĩ một cái gì đó như sau, sử dụng clojure.contrib.str-utils2/replace
, sẽ giúp:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
vào nó ... và nó cũng không sử dụng clojure.string/replace
chức năng tích hợp.
Điều này không hoàn hảo, nhưng đây là một cái gì đó với filter
, Character/isDigit
và Integer/parseInt
. Nó sẽ không hoạt động đối với các số dấu phẩy động và nó sẽ thất bại nếu không có chữ số trong đầu vào, vì vậy bạn có thể nên xóa nó. Tôi hy vọng có một cách tốt hơn để làm điều này mà không liên quan đến quá nhiều Java.
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)
Tôi có thể sẽ thêm một vài điều vào các yêu cầu:
Có lẽ một cái gì đó như:
(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
và sau đó có lẽ là điểm thưởng cho việc biến điều này thành một phương thức đa cho phép mặc định do người dùng cung cấp khác 0.
Mở rộng câu trả lời của snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Phiên bản này trả về nil nếu không có chữ số trong đầu vào, thay vì đưa ra một ngoại lệ.
Câu hỏi của tôi là liệu có thể chấp nhận viết tắt tên thành "str-> int" hay không, nếu những thứ như thế này phải luôn được chỉ định đầy đủ.
Ngoài ra, sử dụng (re-seq)
hàm có thể mở rộng giá trị trả về thành một chuỗi chứa tất cả các số hiện có trong chuỗi đầu vào theo thứ tự:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
Câu hỏi hỏi về phân tích một chuỗi thành một số.
(number? 0.5)
;;=> true
Vì vậy, từ các số thập phân ở trên nên được phân tích cú pháp là tốt.
Có lẽ không trả lời chính xác câu hỏi bây giờ, nhưng để sử dụng chung, tôi nghĩ rằng bạn sẽ muốn nghiêm ngặt về việc đó có phải là số hay không (vì vậy "px" không được phép) và để người gọi xử lý các số không bằng cách trả về nil:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
Và nếu Floats có vấn đề cho tên miền của bạn thay vì Float/parseFloat
đặt bigdec
hoặc một cái gì đó khác.
Đối với bất kỳ ai khác muốn phân tích một chuỗi ký tự bình thường hơn thành một số, nghĩa là một chuỗi không có các ký tự không phải là số khác. Đây là hai cách tiếp cận tốt nhất:
Sử dụng 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")
Điều này cho phép bạn kiểm soát chính xác loại bạn muốn phân tích số, khi đó là vấn đề quan trọng đối với trường hợp sử dụng của bạn.
Sử dụng trình đọc Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Không giống như sử dụng read-string
từ clojure.core
đó không an toàn để sử dụng cho đầu vào không tin cậy, edn/read-string
an toàn để chạy trên đầu vào không tin cậy như đầu vào của người dùng.
Điều này thường thuận tiện hơn khi sử dụng Java nếu bạn không cần phải kiểm soát cụ thể các loại. Nó có thể phân tích bất kỳ số nào theo nghĩa đen mà Clojure có thể phân tích cú pháp như:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Danh sách đầy đủ tại đây: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Đối với các trường hợp đơn giản, bạn chỉ cần sử dụng biểu thức chính quy để lấy chuỗi chữ số đầu tiên như đã đề cập ở trên.
Nếu bạn có một tình huống phức tạp hơn, bạn có thể muốn sử dụng thư viện InstaPude:
(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)
thay vì bắt người dùng phải làm gì (:require [tupelo.core :refer :all])
?
refer-tupelo
được mô phỏng theo refer-clojure
, trong đó nó không bao gồm mọi thứ theo cách (:require [tupelo.core :refer :all])
đó.