In Clojure how can I convert a String to a number?

前端 未结 13 654
别跟我提以往
别跟我提以往 2020-12-12 15:34

I have various strings, some like \"45\", some like \"45px\". How how I convert both of these to the number 45?

相关标签:
13条回答
  • 2020-12-12 16:00

    For simple cases you can just use a regex to pull out the first string of digits as mentioned above.

    If you have a more complicated situation you may wish to use the InstaParse library:

    (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"))))
    
    0 讨论(0)
  • 2020-12-12 16:01

    This works in repl for me, much more straight forward.

    (read-string "123")

    => 123

    0 讨论(0)
  • 2020-12-12 16:01

    AFAIK there's no standard solution for your problem. I think something like the following, which uses clojure.contrib.str-utils2/replace, should help:

    (defn str2int [txt]
      (Integer/parseInt (replace txt #"[a-zA-Z]" "")))
    
    0 讨论(0)
  • 2020-12-12 16:04

    I would probably add a few things to the requirements:

    • Has to start with a digit
    • Has to tolerate empty inputs
    • Tolerates being passed any object (toString is standard)

    Maybe something like:

    (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
    

    and then perhaps bonus points for making this a multi-method that allows for a user-supplied default other than 0.

    0 讨论(0)
  • 2020-12-12 16:08

    For anyone else looking to parse a more normal String literal into a number, that is, a string which doesn't have other non numeric characters. These are the two best approaches:

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

    This lets you precisely control the type you want to parse the number in, when that matters to your use case.

    Using the Clojure EDN reader:

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

    Unlike using read-string from clojure.core which isn't safe to use on untrusted input, edn/read-string is safe to run on untrusted input such as user input.

    This is often more convenient then the Java interop if you don't need to have specific control of the types. It can parse any number literal that Clojure can parse such as:

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

    Full list here: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers

    0 讨论(0)
  • 2020-12-12 16:10

    Also using (re-seq) function can extend the return value to a string containing all the numbers existing in the input string in order:

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

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

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

    0 讨论(0)
提交回复
热议问题