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


128

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


32
누군가 기본적인 질문을하는 것을 두려워하지 않아서 다행입니다.
octopusgrabbus

4
+1-도전 과제의 일부는 Clojure 문서가 때때로 다른 언어로 당연하게 여겨지는 이러한 "기본"질문을 다루지 않는다는 것입니다. (3 년 후 같은 질문을했고 이것을 찾았습니다).
Glenn

3
@octopusgrabbus-사람들이 기본적인 질문을하는 것을 두려워하는 "왜"에 대해 관심이 있습니까?
appshare.co

1
@Zubair 그것은 기본적인 것들이 이미 어딘가에 설명되어 있다고 가정합니다. 그래서 당신은 아마도 무엇인가 간과했을 것이고 당신의 질문은 "연구 노력이 없다"는 말로 내려 질 것입니다.
Al.G.

1
변환하고자하는 구글에서 여기 오는 분들 "9"9, 이것은 나를 위해 일한 가장 좋은 방법이다 (Integer. "9").
weltschmerz 2014

답변:


79

이 작업 10px또는px10

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

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

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

좋은 대답입니다! 내 의견으로는 문자열을 읽는 것보다 낫습니다. 귀하의 기술을 사용하도록 답변을 변경했습니다. 나는 몇 가지 작은 변화도했다.
Benjamin Atkin

이것은 나에게Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
maazza

83

새로운 답변

나는 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

나는 당신의 대답이 가장 좋습니다-너무 나쁜 것은 clojure 핵심 라이브러리에서 제공하지 않습니다. 하나의 사소한 비판-기술적으로 당신 의 fns에 다른 블록이 없기 때문에 if있어야합니다 when.
quux00

1
예, 첫 번째 또는 두 번째 코드 스 니펫 이후에 읽지 마십시오.
Benjamin Atkin

2
앞에 0이 붙은 숫자가 표시됩니다. read-string그것들을 8 진수로 해석합니다 : (read-string "08")예외를 던집니다. Integer/valueOf소수점 이하 자릿수로 처리합니다. (Integer/valueOf "08")8로 평가됩니다.
rubasov

참고 또한이 read-string당신이 그것을 "29px"와 같은 빈 문자열이나 뭐 줄 경우 예외가 발생합니다
일리아 Boyandin

그렇습니다. 질문 본문에있는 질문에 대답하기 전에 제목의 질문에 대한 답변과이 페이지를 볼 때 사람들이 기대하는 내용에 답했습니다. 내 답변 본문의 마지막 코드 스 니펫입니다.
Benjamin Atkin

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/valueOfInteger 생성자 대신 을 사용하는 것이 좋습니다 . Integer 클래스는 객체 생성을 최소화하기 위해 -128에서 127 사이의 값을 캐시합니다. Integer Javadoc은 이것을 다음 게시물과 같이 설명합니다 : stackoverflow.com/a/2974852/871012
quux00

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기능 을 사용하지 않습니다 .
타르

8

완벽하지는 않지만 여기에 뭔가가 있습니다. 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)

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

그런 다음이 방법을 다중 방법으로 만들면 보너스 포인트가 아닌 사용자 제공 기본값을 사용할 수 있습니다.


4

snrobot의 답변 확대 :

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

이 버전은 입력에 숫자가 없으면 예외를 발생시키지 않고 nil을 반환합니다.

내 질문은 이름을 "str-> int"로 축약 할 수 있는지 또는 이와 같은 것을 항상 완전히 지정해야하는지 여부입니다.


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


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/parseFloatbigdec또는 뭔가 다른.


3

더 일반적인 문자열 리터럴을 숫자, 즉 숫자가 아닌 다른 문자가없는 문자열로 구문 분석하려는 다른 사람. 다음은 두 가지 가장 좋은 방법입니다.

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

이를 통해 유스 케이스에 중요한 숫자를 구문 분석하려는 유형을 정확하게 제어 할 수 있습니다.

Clojure EDN 리더 사용 :

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

사용 달리 read-string에서 clojure.core어떤 신뢰할 수없는 입력을 사용하는 것이 안전하지 않습니다, edn/read-string같은 사용자 입력로 신뢰할 수없는 입력에서 실행하는 것이 안전합니다.

유형을 구체적으로 제어 할 필요가없는 경우 Java interop보다 더 편리합니다. 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-tupelorefer-clojure방법은 모든 것을 포함하지 않는다는 점 에서을 모델로 (:require [tupelo.core :refer :all])합니다.
Alan Thompson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.