我有各种字符串,有些像“ 45”,有些像“ 45px”。如何将它们都转换为数字45?
"9"
为的人来说9
,这对我来说是最好的选择:(Integer. "9")
。
我有各种字符串,有些像“ 45”,有些像“ 45px”。如何将它们都转换为数字45?
"9"
为的人来说9
,这对我来说是最好的选择:(Integer. "9")
。
Answers:
我更喜欢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
if
应该是a,when
因为您的fns中没有其他障碍。
read-string
将它们解释为八进制:(read-string "08")
引发异常。Integer/valueOf
将其视为十进制:(Integer/valueOf "08")
计算结果为
read-string
如果您给它提供一个空字符串或类似“ 29px”的内容
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
,而不要使用Integer构造函数。Integer类在-128到127之间缓存值,以最大程度地减少对象创建。Integer Javadoc对此描述如下:stackoverflow.com/a/2974852/871012
这对我来说很有效,更直接。
(读取字符串“ 123”)
=> 123
read-string
可以按照文档执行代码:clojuredocs.org/clojure.core/read-string
AFAIK没有针对您问题的标准解决方案。我认为使用的以下内容clojure.contrib.str-utils2/replace
应会有所帮助:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
它为止……而且它也不会利用内置clojure.string/replace
函数。
这不是完美的,但这里的东西filter
,Character/isDigit
和Integer/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)
我可能会在要求中添加一些内容:
也许像:
(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)的加分。
扩展snrobot的答案:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
如果输入中没有数字,则此版本返回nil,而不是引发异常。
我的问题是将名称缩写为“ str-> int”是否可以接受,或者是否应该始终完全指定类似的名称。
也(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
该问题询问有关将字符串解析为数字的问题。
(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
或其他问题。
对于希望将更普通的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-string
from 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
对于简单的情况,您可以如上所述使用正则表达式提取第一个数字字符串。
如果您遇到的情况更加复杂,则可能希望使用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])
?
refer-tupelo
是根据建模的refer-clojure
,因为它不包括该方法所包含的所有内容(:require [tupelo.core :refer :all])
。