我一直在使用java来解析数字,例如
(. Integer parseInt numberString)
Run Code Online (Sandbox Code Playgroud)
是否有更多的clojuriffic方式可以处理整数和浮点数,并返回clojure数字?我并不特别担心这里的性能,我只想在一个文件中处理一堆空白分隔的数字,并以最简单的方式对它们做一些事情.
所以文件可能有如下行:
5 10 0.0002
4 12 0.003
Run Code Online (Sandbox Code Playgroud)
而且我希望能够将线条转换为数字向量.
Bri*_*per 64
如果您非常确定您的文件只包含数字,则可以使用Clojure阅读器来解析数字.这样做的好处是可以在需要时为您提供浮子或Bignums.
user> (require '[clojure.edn :as edn])
nil
user> (edn/read-string "0.002")
0.0020
Run Code Online (Sandbox Code Playgroud)
如果您正在解析用户提供的任意输入,这是不安全的,因为读取器宏可用于在读取时执行任意代码并删除您的硬盘驱动器等.
如果你想要一个巨大的数字向量,你可以作弊并这样做:
user> (let [input "5 10 0.002\n4 12 0.003"]
(read-string (str "[" input "]")))
[5 10 0.0020 4 12 0.0030]
Run Code Online (Sandbox Code Playgroud)
虽然有点hacky.或者有re-seq:
user> (let [input "5 10 0.002\n4 12 0.003"]
(map read-string (re-seq #"[\d.]+" input)))
(5 10 0.0020 4 12 0.0030)
Run Code Online (Sandbox Code Playgroud)
或者每行一个向量:
user> (let [input "5 10 0.002\n4 12 0.003"]
(for [line (line-seq (java.io.BufferedReader.
(java.io.StringReader. input)))]
(vec (map read-string (re-seq #"[\d.]+" line)))))
([5 10 0.0020] [4 12 0.0030])
Run Code Online (Sandbox Code Playgroud)
我相信还有其他方法.
laz*_*zy1 25
如果您想更安全,可以使用Float/parseFloat
user=> (map #(Float/parseFloat (% 0)) (re-seq #"\d+(\.\d+)?" "1 2.2 3.5"))
(1.0 2.2 3.5)
user=>
Run Code Online (Sandbox Code Playgroud)
Mic*_*zyk 25
不确定这是否是"最简单的方式",但我觉得它很有趣,所以...通过反射黑客,你可以只访问Clojure读者的数字阅读部分:
(let [m (.getDeclaredMethod clojure.lang.LispReader
"matchNumber"
(into-array [String]))]
(.setAccessible m true)
(defn parse-number [s]
(.invoke m clojure.lang.LispReader (into-array [s]))))
Run Code Online (Sandbox Code Playgroud)
然后使用如下:
user> (parse-number "123")
123
user> (parse-number "123.5")
123.5
user> (parse-number "123/2")
123/2
user> (class (parse-number "123"))
java.lang.Integer
user> (class (parse-number "123.5"))
java.lang.Double
user> (class (parse-number "123/2"))
clojure.lang.Ratio
user> (class (parse-number "123123451451245"))
java.lang.Long
user> (class (parse-number "123123451451245123514236146"))
java.math.BigInteger
user> (parse-number "0x12312345145124")
5120577133367588
user> (parse-number "12312345142as36146") ; note the "as" in the middle
nil
Run Code Online (Sandbox Code Playgroud)
请注意,NumberFormatException如果出现问题,这不会引起常规; nil如果你愿意的话,你可以自己添加支票并自己扔.
至于性能,让我们有一个不科学的微基准标记(两个函数都已"预热";初始运行像往常一样慢):
user> (time (dotimes [_ 10000] (parse-number "1234123512435")))
"Elapsed time: 564.58196 msecs"
nil
user> (time (dotimes [_ 10000] (read-string "1234123512435")))
"Elapsed time: 561.425967 msecs"
nil
Run Code Online (Sandbox Code Playgroud)
明显的免责声明:clojure.lang.LispReader.matchNumber是一种私有静态方法,clojure.lang.LispReader可以随时更改或删除.
小智 19
在我看来,当你想要任何数字时,最好/最安全的方式是有效的,当它不是数字时失败是这样的:
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*$" s)
(read-string s)))
Run Code Online (Sandbox Code Playgroud)
例如
(parse-number "43") ;=> 43
(parse-number "72.02") ;=> 72.02
(parse-number "009.0008") ;=> 9.008
(parse-number "-92837482734982347.00789") ;=> -9.2837482734982352E16
(parse-number "89blah") ;=> nil
(parse-number "z29") ;=> nil
(parse-number "(exploit-me)") ;=> nil
Run Code Online (Sandbox Code Playgroud)
适用于整数,浮点数/双打,bignums等.如果你想增加对其他符号的支持,只需增加正则表达式即可.
Sta*_*ris 15
Brian Carper的建议方法(使用read-string)运行良好,但只有在你尝试解析零填充数字(如"010")之前.注意:
user=> (read-string "010")
8
user=> (read-string "090")
java.lang.RuntimeException: java.lang.NumberFormatException: Invalid number: 090 (NO_SOURCE_FILE:0)
Run Code Online (Sandbox Code Playgroud)
这是因为clojure试图将"090"解析为八进制,而090不是有效的八进制!
car*_*cad 15
Brian Carper的答案几乎是正确的.而不是直接从clojure的核心使用read-string.使用clojure.edn/read-string.它是安全的,它会解析你抛出的任何东西.
(ns edn-example.core
(require [clojure.edn :as edn]))
(edn/read-string "2.7"); float 2.7
(edn/read-string "2"); int 2
Run Code Online (Sandbox Code Playgroud)
简单,容易且执行安全;)
我发现solussd的答案非常适合我的代码.在此基础上,这里有一个增强,支持科学记数法.此外,添加(.trim s)以便可以容忍额外的空间.
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*([Ee]\+\d+|[Ee]-\d+|[Ee]\d+)?$" (.trim s))
(read-string s)))
Run Code Online (Sandbox Code Playgroud)
例如
(parse-number " 4.841192E-002 ") ;=> 0.04841192
(parse-number " 4.841192e2 ") ;=> 484.1192
(parse-number " 4.841192E+003 ") ;=> 4841.192
(parse-number " 4.841192e.2 ") ;=> nil
(parse-number " 4.841192E ") ;=> nil
Run Code Online (Sandbox Code Playgroud)
使用bigint和bigdec
(bigint "1")
(bigint "010") ; returns 10N as expected
(bigint "111111111111111111111111111111111111111111111111111")
(bigdec "11111.000000000000000000000000000000000000000000001")
Run Code Online (Sandbox Code Playgroud)
Clojure bigint 将尽可能使用原语,同时避免使用正则表达式,八进制文字的问题或其他数字类型的有限大小,导致(Integer. "10000000000")失败.
(这最后一件事发生在我身上并且非常令人困惑:我把它包装成一个parse-int函数,然后假设parse-int意味着"解析一个自然整数"而不是"解析一个32位整数")
这是两种最佳且正确的方法:
使用 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")
Run Code Online (Sandbox Code Playgroud)
当这对您的用例很重要时,这使您可以精确地控制要解析数字的类型。
使用 Clojure EDN 阅读器:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Run Code Online (Sandbox Code Playgroud)
与 using read-stringfrom clojure.corewhich 在不受信任的输入上使用edn/read-string不安全不同,在不受信任的输入(例如用户输入)上运行是安全的。
This is often more convenient then the Java interop if you don't need to have specific control of the types. It can parse any number literal that Clojure can parse such as:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Run Code Online (Sandbox Code Playgroud)
Full list here: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
| 归档时间: |
|
| 查看次数: |
44594 次 |
| 最近记录: |