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


128

「45」、「45px」など、さまざまな文字列があります。これらの両方を45に変換する方法を教えてください。


32
誰かが基本的な質問をすることを恐れていないことを嬉しく思います。
タコグラブバス

4
+1-課題の一部は、Clojureのドキュメントが、他の言語で当たり前と見なされているこれらの「基本的な」質問に時々対応しないことです。(3年後に同じ質問があり、これを見つけました)。
Glenn

3
@octopusgrabbus-「なぜ」人々が基本的な質問をすることを恐れているのか知りたいです。
appshare.co 2015年

1
@Zubairそれは基本的なことがすでにどこかで説明されていると思われるので、おそらく見落としがあり、あなたの質問は「研究努力なし」に反対票が投じられるでしょう。
Al.G.

1
ここからGoogleに変換"9"してに変換しようとしている人にとって9、これは私にとってうまくいった最高のものです(Integer. "9")
weltschmerz

回答:


79

これは上で動作します10pxpx10

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

最初の連続する数字のみを解析するので

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

素敵な答え!これは、私の意見ではread-stringを使用するよりも優れています。私はあなたのテクニックを使うように私の答えを変えました。私もいくつかの小さな変更を加えました。
Benjamin Atkin 2013年

これは私に与えるException in thread "main" java.lang.ClassNotFoundException: Integer.,
maazza

83

新しい答え

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

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

さらに、先行ゼロがある場合でも、基数が指定されていない場合、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コアライブラリでは提供されていません。1つのマイナーな批評-技術的にはfnsに他のブロックがないifので、あなたはa whenであるべきです。
quux00

1
ええ、最初または2番目のコードスニペットの後に読むのをやめないでください!
Benjamin Atkin 2013年

2
先行ゼロのある数値のヘッズアップ。read-stringそれらを8進数として解釈します:(read-string "08")例外をスローします。Integer/valueOf:小数として扱い、それらを(Integer/valueOf "08")8と評価する
rubasov

read-string空の文字列または「29px」のようなものを与えると例外がスローされることにも注意してください
Ilya Boyandin

それがあるべきように。タイトルで質問に回答し、このページを表示したときに人々が期待することを質問本体で質問に回答する前に回答しました。これは私の回答の本文の最後のコードスニペットです。
ベンジャミンアトキン2013年

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

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

ありがとう。これは、製品を一連の数字に分割するのに役立ちました。
タコグラブバス

3
この答えはJavaの世界にいるためInteger/valueOf、通常はIntegerコンストラクターではなくを使用することをお勧めします。Integerクラスは、オブジェクトの作成を最小限に抑えるために-128から127までの値をキャッシュします。この記事がするように整数のJavadocは、このことを説明します。stackoverflow.com/a/2974852/871012
quux00

15

これは私にとってreplで機能します。

(読み取り文字列 "123")

=> 123


1
ユーザー入力でこれを使用する場合は注意してください。read-stringドキュメントごとにコードを実行できます:clojuredocs.org/clojure.core/read-string
jerney

これは、プログラミングパズルなどの信頼できる入力に最適です。@jerneyは正しいです。実際のコードでは使用しないように注意してください。
hraban

10

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

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

推奨されません。それは誰かがそれを投げるまで機能します1.5...そしてそれはまた組み込みclojure.string/replace関数を利用しません。
tar

8

これは完璧ではないが、ここで何かをだfilterCharacter/isDigitInteger/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

そして、おそらくこれを、0以外のユーザー指定のデフォルトを可能にするマルチメソッドにするためのボーナスポイント。


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/parseFloatプットbigdecなどの代わりにドメインに問題がある場合。


3

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

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-stringfrom 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


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 2017年

refer-tupeloはをモデルにrefer-clojureして(:require [tupelo.core :refer :all])います。つまり、方法のすべてが含まれているわけではありません。
アラントンプソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.