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

我有各种string,有些像“45”,有些像“45px”。 如何将这两个转换为45号?

这将工作在10pxpx10

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

这不是完美的,但这里有一些filterCharacter/isDigitInteger/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/parseFloatbigdec或其他东西。

我可能会添加一些东西的要求:

  • 必须以数字开头
  • 必须容忍空投入
  • 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"))))