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


128

我有各种字符串,有些像“ 45”,有些像“ 45px”。如何将它们都转换为数字45?


32
我很高兴有人不怕问一些基本问题。
octopusgrabbus

4
+1-挑战的一部分是Clojure文档有时无法解决我们以其他语言视为理所当然的这些“基本”问题。(三年后我遇到了同样的问题,并找到了答案)。
Glenn 2014年

3
@octopusgrabbus-我很想知道“为什么”人们不敢问基本问题?
appshare.co 2015年

1
@Zubair假定已经在某个地方解释了基本的东西,所以您很可能忽略了某些东西,并且您的问题将因“没有研究努力”而被否决。
Al.G.

1
对于那些希望从Google那里转换"9"为的人来说9,这对我来说是最好的选择:(Integer. "9")
weltschmerz

Answers:


79

这将工作于10pxpx10

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

它只会解析第一个连续数字,因此

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

好答案!我认为这比使用读取字符串更好。我更改了答案以使用您的技术。我也做了一些小改动。
本杰明·阿特金

这给了我Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Maazza

83

新答案

我更喜欢snrobot的答案。对于这种简单的用例,使用Java方法比使用读取字符串更简单,更可靠。我确实做了一些小改动。由于作者不排除负数,因此我对其进行了调整,以允许使用负数。我也这样做了,所以它要求数字从字符串的开头开始。

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

另外,我发现,即使没有前导零,Integer / parseInt也会在不提供基数的情况下将其解析为十进制。

旧答案

首先,仅解析一个整数(因为这在Google上很受欢迎,而且是很好的背景信息):

您可以使用阅读器

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

读取后可以检查是否为数字:

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

我不确定clojure阅读器是否可以信任用户输入,因此您也可以在读取之前进行检查:

(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应该是a,when因为您的fns中没有其他障碍。
quux00 2012年

1
是的,请不要在第一个或第二个代码段后停止阅读!
本杰明·阿特金

2
对带有前导零的数字进行单挑。read-string将它们解释为八进制:(read-string "08")引发异常。Integer/valueOf将其视为十进制:(Integer/valueOf "08")计算结果为
8。– rubasov

还请注意,read-string如果您给它提供一个空字符串或类似“ 29px”的内容
则会

正如它应该。在标题正文中回答问题之前,我回答了标题中的问题以及人们看到此页面时的期望。这是我答案正文中的最后一个代码段。
本杰明·阿特金

30
(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对此描述如下:stackoverflow.com/a/2974852/871012
quux00 2012年

15

这对我来说很有效,更直接。

(读取字符串“ 123”)

=> 123


1
小心将其与用户输入一起使用。read-string可以按照文档执行代码:clojuredocs.org/clojure.core/read-string
jerney

这对于受信任的输入非常有用,例如编程难题。@jerney是正确的:小心不要在实际代码中使用它。
hraban

10

AFAIK没有针对您问题的标准解决方案。我认为使用的以下内容clojure.contrib.str-utils2/replace应会有所帮助:

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

不建议。它会一直工作到有人扔掉1.5它为止……而且它也不会利用内置clojure.string/replace函数。
tar

8

这不是完美的,但这里的东西filterCharacter/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)

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)的加分。


4

扩展snrobot的答案:

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

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

我的问题是将名称缩写为“ str-> int”是否可以接受,或者是否应该始终完全指定类似的名称。


3

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

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

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

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


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或其他问题。


3

对于希望将更普通的String文字解析为数字的其他人,即没有其他非数字字符的字符串。这是两种最佳方法:

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

与不安全地在不受信任的输入上使用read-stringfrom clojure.core相比,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


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

refer-tupelo是根据建模的refer-clojure,因为它不包括该方法所包含的所有内容(:require [tupelo.core :refer :all])
艾伦·汤普森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.