簡體   English   中英

在 Clojure 中,如何將字符串轉換為數字?

[英]In Clojure how can I convert a String to a number?

我有各種字符串,有些像“45”,有些像“45px”。 我如何將這兩個轉換為數字 45?

這將適用於10pxpx10

(defn parse-int [s]
   (Integer. (re-find  #"\d+" s )))

它只會解析第一個連續數字

user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10

新答案

我更喜歡snrobot的回答。 對於這個簡單的用例,使用 Java 方法比使用讀取字符串更簡單、更健壯。 我確實做了一些小改動。 由於作者沒有排除負數,所以我調整了一下,允許負數。 我也做了它,所以它要求數字從字符串的開頭開始。

(defn parse-int [s]
  (Integer/parseInt (re-find #"\A-?\d+" s)))

此外,我發現 Integer/parseInt 在沒有給出基數時解析為十進制,即使有前導零也是如此。

舊答案

首先,只解析一個整數(因為這在谷歌上很受歡迎,而且是很好的背景信息):

您可以使用閱讀器

(read-string "9") ; => 9

您可以在閱讀后檢查它是否是一個數字:

(defn str->int [str] (if (number? (read-string str))))

我不確定 clojure 閱讀器是否可以信任用戶輸入,因此您也可以在閱讀之前進行檢查:

(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))

我想我更喜歡最后一個解決方案。

現在,針對您的具體問題。 要解析以整數開頭的內容,例如29px

(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
(defn parse-int [s]
  (Integer. (re-find #"[0-9]*" s)))

user> (parse-int "10px")
10
user> (parse-int "10")
10

這對我來說在 repl 中有效,更直接。

(讀取字符串“123”)

=> 123

AFAIK 沒有針對您的問題的標准解決方案。 我認為使用clojure.contrib.str-utils2/replace類似以下內容應該有所幫助:

(defn str2int [txt]
  (Integer/parseInt (replace txt #"[a-zA-Z]" "")))

這並不完美,但這里有一些帶有filterCharacter/isDigitInteger/parseInt 它不適用於浮點數,如果輸入中沒有數字,它就會失敗,所以你應該清理它。 我希望有一種更好的方法來做到這一點,而不涉及太多 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)

我可能會在要求中添加一些內容:

  • 必須以數字開頭
  • 必須容忍空輸入
  • 容忍傳遞任何對象(toString 是標准的)

也許是這樣的:

(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

然后也許可以使這成為一種允許用戶提供的默認值而不是 0 的多方法的獎勵積分。

擴展snrobot的答案:

(defn string->integer [s] 
  (when-let [d (re-find #"-?\d+" s)] (Integer. d)))

如果輸入中沒有數字,則此版本返回 nil,而不是引發異常。

我的問題是將名稱縮寫為“str->int”是否可以接受,或者是否應該始終完全指定此類內容。

對於希望將更普通的字符串文字解析為數字的其他人,即沒有其他非數字字符的字符串。 這是兩個最好的方法:

使用 Java 互操作:

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

當這對您的用例很重要時,這使您可以精確地控制要解析數字的類型。

使用 Clojure EDN 閱讀器:

(require '[clojure.edn :as edn])
(edn/read-string "333")

與使用clojure.core read-string不同,在不受信任的輸入上使用是不安全的, edn/read-string在不受信任的輸入(例如用戶輸入)上運行是安全的。

如果您不需要對類型進行特定控制,這通常比 Java 互操作更方便。 它可以解析 Clojure 可以解析的任何數字文字,例如:

;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")

完整列表在這里: https : //www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers

同樣使用(re-seq)函數可以將返回值擴展為包含輸入字符串中存在的所有數字的字符串:

(defn convert-to-int [s] (->> (re-seq #"\\d" s) (apply str) (Integer.)))

(convert-to-int "10not123") => 10123

(type *1) => java.lang.Integer

該問題詢問有關將字符串解析為數字的問題。

(number? 0.5)
;;=> true

所以從上面的小數也應該被解析。

也許現在不能完全回答這個問題,但對於一般用途,我認為您應該嚴格確定它是否是數字(因此不允許使用“px”)並讓調用者通過返回 nil 來處理非數字:

(defn str->number [x]
  (when-let [num (re-matches #"-?\d+\.?\d*" x)]
    (try
      (Float/parseFloat num)
      (catch Exception _
        nil))))

如果 Floats 對您的域而不是Float/parseFloat有問題,請放置bigdec或其他東西。

對於簡單的情況,您可以使用正則表達式來提取上述第一串數字。

如果您有更復雜的情況,您可能希望使用 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"))))

這個如何避免某些字符串的異常?

(defn string-to-number [in]
  (let [s (strip-whitespace in)      ;; trim
        f (re-find #"\d+" s)]        ;; search digit else nil
    (if f (Integer/parseInt f) 0)))  ;; if not-nil do cast

(string-to-number "-")
(string-to-number "10")
(string-to-number "px10")
(string-to-number "1200 xr")

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM