在Clojure中,如何将String转换为数字?

时间:2011-04-11 12:30:21

标签: clojure

我有各种各样的字符串,有些像“45”,有些像“45px”。如何将这两者转换为数字45?

13 个答案:

答案 0 :(得分:78)

新答案

我更喜欢snrobot的答案。对于这个简单的用例,使用Java方法比使用read-string更简单,更健壮。我确实做了一些小改动。由于作者没有排除负数,我调整它以允许负数。我也这样做了它需要从字符串的开头开始编号。

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

此外,我发现Integer / parseInt在没有给出基数时解析为十进制,即使有前导零也是如此。

旧答案

首先,要解析一个整数(因为这是谷歌的一个命中,它是很好的背景信息):

您可以使用reader

(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

答案 1 :(得分:76)

这适用于10pxpx10

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

它只解析第一个连续数字

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

答案 2 :(得分:28)

(defn parse-int [s]
  (Integer. (re-find #"[0-9]*" s)))

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

答案 3 :(得分:15)

这对我来说很复杂,更直接。

(读取字符串“123”)

=> 123

答案 4 :(得分:9)

AFAIK没有针对您的问题的标准解决方案。我认为使用clojure.contrib.str-utils2/replace的以下内容应该有所帮助:

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

答案 5 :(得分:7)

这不完美,但这里有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)

答案 6 :(得分:3)

问题是要求将字符串解析为数字。

(number? 0.5)
;;=> true

所以从上面的小数也应该被解析。

或许现在还没有完全回答这个问题,但是对于一般用途,我认为你会想要严格判断它是否是一个数字(所以" px"不允许)并让调用者处理非-numbers返回nil:

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

如果Floats对您的域名有问题,而不是Float/parseFloat放置bigdec或其他内容。

答案 7 :(得分:2)

我可能会在要求中添加一些内容:

  • 必须以数字开头
  • 必须容忍空输入
  • 容忍传递任何对象(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。

答案 8 :(得分:2)

扩展snrobot的答案:

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

如果输入中没有数字,则此版本返回nil,而不是引发异常。

我的问题是,将名称缩写为“str-> int”是否可以接受,或者是否应始终完全指定此类内容。

答案 9 :(得分:2)

同样使用(re-seq)函数可以将返回值扩展为包含输入字符串中存在的所有数字的字符串:

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

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

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

答案 10 :(得分:2)

如何避免在某些字符串上出现异常?

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

答案 11 :(得分:1)

对于简单的情况,您可以使用正则表达式来提取上面提到的第一个数字字符串。

如果您遇到更复杂的情况,您可能希望使用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"))))

答案 12 :(得分:0)

对于希望将更普通的String文字解析为数字的任何其他人,即没有其他非数字字符的字符串。这是两种最佳方法:

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

与使用read-string中的clojure.core(在不可信输入中使用不安全)不同,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