在Clojure中,如何将string转换为数字?
我有各种string,有些像“45”,有些像“45px”。 如何将这两个转换为45号?
这将工作在10px
或px10
(defn parse-int [s] (Integer. (re-find #"\d+" s )))
它只会parsing第一个连续的数字
user=> (parse-int "10not123") 10 user=> (parse-int "abc10def11") 10
新的答案
我更喜欢snrobot的答案。 对于这个简单的用例,使用Java方法比使用read-string更简单,更健壮。 我做了一些小的改变。 由于作者不排除负数,我调整了允许负数。 我也是这样做的,所以它需要从string的开始处开始编号。
(defn parse-int [s] (Integer/parseInt (re-find #"\A-?\d+" s)))
此外,我发现Integer / parseIntparsing为十进制时,没有基数,即使有前导零。
老答案
首先,parsing一个整数(因为这是一个在谷歌的打击,这是很好的背景信息):
你可以使用读者 :
(read-string "9") ; => 9
你可以在阅读后检查它是否是一个数字:
(defn str->int [str] (if (number? (read-string str))))
我不确定用户input是否可以被clojure阅读器信任,因此您可以在阅读之前进行检查:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
我想我更喜欢最后的解决scheme。
而现在,你的具体问题。 parsing以整数开头的内容,如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
AFAIK没有针对您的问题的标准解决scheme。 我觉得像下面这样,使用clojure.contrib.str-utils2/replace
,应该有所帮助:
(defn str2int [txt] (Integer/parseInt (replace txt #"[a-zA-Z]" "")))
这不是完美的,但这里有一些filter
, Character/isDigit
和Integer/parseInt
。 它不适用于浮点数,如果input中没有数字,它就会失败,所以你应该清理它。 我希望有一个更好的方法来做到这一点,不涉及太多的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)
扩大snrobot的答案:
(defn string->integer [s] (when-let [d (re-find #"-?\d+" s)] (Integer. d)))
如果input中没有数字,则此版本返回nil,而不是引发exception。
我的问题是,是否可以将名称缩写为“str-> int”,或者是否应该总是指定这样的事情。
这对我来说很重要,更直截了当。
(读取string“123”)
=> 123
同样使用(re-seq)
函数可以将返回值扩展为包含inputstring中存在的所有数字的string,顺序如下:
(defn convert-to-int [s] (->> (re-seq #"\d" s) (apply str) (Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
问题是关于把一个stringparsing成一个数字。
(number? 0.5) ;;=> true
所以从上面的小数也应该被parsing。
也许现在不是完全回答这个问题,但对于一般用途,我想你会想要严格一下是否是一个数字(所以“px”不允许),并让调用者通过返回nil来处理非数字:
(defn str->number [x] (when-let [num (re-matches #"-?\d+\.?\d*" x)] (try (Float/parseFloat num) (catch Exception _ nil))))
如果Float/parseFloat
对你的域有问题,而不是Float/parseFloat
把bigdec
或其他东西。
我可能会添加一些东西的要求:
- 必须以数字开头
- 必须容忍空投入
- Tolerates传递任何对象(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。
对于简单的情况,你可以使用正则expression式来提取第一串数字,如上所述。
如果你有更复杂的情况,你可能希望使用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"))))