web-dev-qa-db-ja.com

Clojureで文字列を数値に変換するにはどうすればよいですか?

「45」のような文字列、「45px」のような文字列があります。これらの両方を45に変換するにはどうすればよいですか?

120
Zubair

これは10pxまたはpx10で動作します

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

最初の連続した数字のみを解析します

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

新しい答え

私はsnrobotの答えがより好きです。 Javaメソッドの使用は、この単純なユースケースでread-stringを使用するよりも簡単で堅牢です。いくつかの小さな変更を加えました。著者は負の数を除外しなかったため、負の数を許可するように調整しました。また、文字列の先頭から番号を開始する必要があるように作成しました。

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

さらに、先行ゼロがある場合でも、基数が指定されていない場合、Integer/parseIntは10進数として解析されることがわかりました。

古い答え

まず、整数だけを解析します(これはグーグルのヒットであり、背景情報が良いため):

reader を使用できます。

(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
79
Benjamin Atkin
(defn parse-int [s]
  (Integer. (re-find #"[0-9]*" s)))

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

これはreplで機能します。もっと簡単です。

(読み取り文字列「123」)

=> 123

15
f3rz_net

私の知る限り、あなたの問題に対する標準的な解決策はありません。 clojure.contrib.str-utils2/replace を使用する次のようなものが役立つと思います:

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

これは完全ではありませんが、filterCharacter/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)
7
michiakig

質問は、文字列を数値に解析することについて尋ねます。

(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 put bigdecまたは他の何かではなく、Floatがドメインに問題がある場合。

3
Chris Murphy

また、(re-seq)関数を使用すると、入力文字列に存在するすべての数字を順番に含む文字列に戻り値を拡張できます。

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

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

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

2
amirt

私はおそらく要件にいくつかのことを追加します:

  • 数字で始まる必要があります
  • 空の入力を許容する必要がある
  • 任意のオブジェクトが渡されることを許容します(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以外のデフォルトを許可するマルチメソッドにするためのボーナスポイント。

2
Tony K.

Snrobotの答えを拡張:

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

このバージョンは、入力に数字がない場合、例外を発生させるのではなく、nilを返します。

私の質問は、名前を「str-> int」に短縮してもよいのか、またはこのようなものを常に完全に指定すべきかということです。

2
Nick Vargish

単純な場合、正規表現を使用して、上記のように数字の最初の文字列を引き出すことができます。

より複雑な状況がある場合は、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"))))
1
Alan Thompson

より通常の文字列リテラルを数値、つまり、他の非数字文字を持たない文字列に解析しようとする他の人のために。これらは2つの最良のアプローチです。

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-stringclojure.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

0
Didier A.