在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个回答

90

这将适用于 10pxpx10

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

它将仅解析第一个连续数字,所以

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

很好的答案!我认为这比使用read-string更好。我改用了你的技巧来回答。我还做了一些小修改。 - Benjamin Atkin
2
这给了我一个异常:Exception in thread "main" java.lang.ClassNotFoundException: Integer. - maazza

89

新回答

我更喜欢snrobot的答案。对于这个简单的用例来说,使用Java方法比使用read-string更简单和更健壮。我进行了一些小修改。由于作者没有排除负数,所以我将其调整为允许负数。我还使它要求数字从字符串开头开始。

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

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

旧回答

首先,为了解析整数(因为这是谷歌搜索的结果并且是良好的背景信息):

您可以使用读取器

(read-string "9") ; => 9

在读取后,您可以检查它是否为数字:

(defn str->int [str] (if (number? (read-string str))))

我不确定Clojure reader是否能够信任用户输入,因此您可以在读取之前进行检查:

(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

我最喜欢你的答案 - 可惜这不是由Clojure核心库提供的。一个小小的批评 - 从技术上讲,你的if应该是一个when,因为在你的fns中没有else块。 - quux00
1
是的,请不要在第一或第二个代码片段后停止阅读! - Benjamin Atkin
2
关于前导零的数字问题需要注意。read-string会将它们解释为八进制:(read-string "08")会抛出异常。而Integer/valueOf则将其视为十进制:(Integer/valueOf "08")的结果为8。 - rubasov
请注意,如果您向read-string提供空字符串或类似于“29px”的内容,则会引发异常。 - Ilya Boyandin
你可以在正则表达式中使用 ^ 替代零宽断言。 - Tony K.
显示剩余2条评论

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

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

谢谢。这对我把一个产品拆分成数字序列很有帮助。 - octopusgrabbus
3
在Java环境中,建议使用Integer/valueOf而非Integer构造函数。Integer类会缓存-128到127之间的值,以最小化对象创建。Integer Javadoc和此文章(链接为https://dev59.com/jHA85IYBdhLWcg3wBO7h#2974852)都有描述这一点。 - quux00

16

对我来说,在repl中这很有效,更加简单。

(read-string "123")

=> 123


4
使用此函数处理用户输入时请小心。根据文档,read-string 函数可以执行代码。详情请见:https://clojuredocs.org/clojure.core/read-string - jerney
这对于可信的输入非常好,例如编程谜题。@jerney是正确的:小心不要在实际代码中使用它。 - hraban

11

据我所知,针对您的问题并没有标准解决方案。我认为以下代码可以帮助解决,它使用了clojure.contrib.str-utils2/replace

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

1
不建议这样做。它可以工作,直到有人将 1.5 传递进来...而且它也没有使用内置的 clojure.string/replace 函数。 - tar

8

这并不完美,但是下面的方法利用了filter, Character/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

如果其他人想将一个更普通的字符串字面值解析为数字,即一个不包含其他非数值字符的字符串。以下是两种最好的方法:

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

与使用来自clojure.core的read-string不安全的输入不同,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


5

对snrobot的回答进行进一步扩展:

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

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

我的问题是是否可以将名称缩写为“str->int”,或者像这样的东西应该始终完全指定。


4

我认为需要增加一些要求:

  • 必须以数字开头
  • 能够容忍空输入
  • 可以接受任何对象(标准的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,那就更好了。


3

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

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

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

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


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