IT story

Clojure에서 문자열을 숫자로 변환하는 방법은 무엇입니까?

hot-time 2020. 7. 13. 07:58
반응형

Clojure에서 문자열을 숫자로 변환하는 방법은 무엇입니까?


"45"와 같은 "45px"와 같은 다양한 문자열이 있습니다. 이 두 가지를 어떻게 숫자 45로 변환합니까?


이 작업 10px또는px10

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

첫 번째 연속 숫자 만 구문 분석합니다.

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

새로운 답변

나는 snrobot의 대답이 더 좋습니다. Java 메소드를 사용하는 것이이 간단한 사용 사례에 대해 읽기 문자열을 사용하는 것보다 더 간단하고 강력합니다. 몇 가지 작은 변경을했습니다. 저자가 음수를 배제하지 않았기 때문에 음수를 허용하도록 조정했습니다. 또한 문자열의 시작 부분에서 숫자를 시작해야합니다.

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

또한 선행 0이 있더라도 기수가 제공되지 않으면 Integer / parseInt가 10 진수로 구문 분석됩니다.

이전 답변

먼저 정수를 구문 분석하려면 (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

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

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

이것은 훨씬 더 직설적으로 나를 위해 작동합니다.

(읽기 문자열 "123")

=> 123


AFAIK 문제에 대한 표준 솔루션이 없습니다. 을 사용하는 다음과 같은 clojure.contrib.str-utils2/replace것이 도움이 될 것이라고 생각합니다 .

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

This isn't perfect, but here's something with filter, Character/isDigit and Integer/parseInt. It won't work for floating point numbers and it fails if there is no digit in the input, so you should probably clean it up. I hope there's a nicer way of doing this that doesn't involve so much 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)

The question asks about parsing a string into a number.

(number? 0.5)
;;=> true

So from the above decimals ought to be parsed as well.

Perhaps not exactly answering the question now, but for general use I think you would want to be strict about whether it is a number or not (so "px" not allowed) and let the caller handle non-numbers by returning nil:

(defn str->number [x]
  (when-let [num (re-matches #"-?\d+\.?\d*" x)]
    (try
      (Float/parseFloat num)
      (catch Exception _
        nil))))

And if Floats are problematic for your domain instead of Float/parseFloat put bigdec or something else.


I would probably add a few things to the requirements:

  • Has to start with a digit
  • Has to tolerate empty inputs
  • Tolerates being passed any object (toString is standard)

Maybe something like:

(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

and then perhaps bonus points for making this a multi-method that allows for a user-supplied default other than 0.


Expanding on snrobot's answer:

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

This versions returns nil if there are no digits in the input, rather than raising an exception.

My question is whether it's acceptable to abbreviate the name to "str->int", or if things like this should always be fully specified.


Also using (re-seq) function can extend the return value to a string containing all the numbers existing in the input string in order:

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

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

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


For simple cases you can just use a regex to pull out the first string of digits as mentioned above.

If you have a more complicated situation you may wish to use the InstaParse library:

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

For anyone else looking to parse a more normal String literal into a number, that is, a string which doesn't have other non numeric characters. These are the two best approaches:

Using Java interop:

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

This lets you precisely control the type you want to parse the number in, when that matters to your use case.

Using the Clojure EDN reader:

(require '[clojure.edn :as edn])
(edn/read-string "333")

Unlike using read-string from clojure.core which isn't safe to use on untrusted input, edn/read-string is safe to run on untrusted input such as user input.

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

Full list here: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers

참고URL : https://stackoverflow.com/questions/5621279/in-clojure-how-can-i-convert-a-string-to-a-number

반응형