在Clojure中,我该如何将一个字符串转换为数字?

148

我有不同的字符串,像“45”和“45px”。如何将这两个字符串转换为数字45?


46
我很高兴有人不害怕提出一些基本问题。 - octopusgrabbus
4
挑战的一部分是Clojure文档有时没有涉及我们在其他语言中认为理所当然的“基本”问题。(我在3年后也有同样的问题,并找到了这个答案)。 - Glenn
3
@octopusgrabbus - 我很想知道为什么人们害怕问基本问题? - yazzapps.com
1
@Zubair 应该已经有一些基础知识的解释了,所以你很可能忽略了什么,你的问题将因为“没有研究努力”而被投票降低。 - Al.G.
@Al.G. 是的,我认为你是正确的。我当时做了调查,但无法在任何地方找到答案。如果您可以给我发送一个解释它的链接,那将不胜感激。 - yazzapps.com
2
对于那些从谷歌搜索想要将"9"转换为9的人,这是我使用过最好的方法:(Integer. "9") - Dennis Hackethal
13个回答

3
该问题涉及将字符串解析为数字。
(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))))

如果浮点数在您的领域中存在问题,可以将Float/parseFloat替换为bigdec或其他内容。


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

2

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

如果您遇到更复杂的情况,可以使用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)而不是让用户执行(:require [tupelo.core :refer :all])呢? - Qwerp-Derp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接