Clojure'da bir String'i sayıya nasıl dönüştürebilirim?


128

Bazı dizelerim var, bazıları "45", bazıları "45px" gibi. Bunların ikisini de 45 sayısına nasıl dönüştürürüm?


32
Birinin bazı temel soruları sormaktan korkmamasına sevindim.
octopusgrabbus

4
+1 - zorluğun bir kısmı, Clojure belgelerinin bazen diğer dillerde hafife aldığımız bu "temel" soruları ele almamasıdır. (Aynı soruyu 3 yıl sonra sordum ve bunu buldum).
Glenn

3
@octopusgrabbus - İnsanların "neden" temel sorular sormaktan korktuğunu merak ediyorum.
appshare.co

1
@Zubair, temel şeylerin zaten bir yerde açıklandığı varsayılıyor, bu yüzden büyük olasılıkla bir şeyi gözden kaçırdınız ve sorunuz "araştırma çabası yok" olarak düşürülecek.
Al.G.

1
Dönüştürmek isteyen Google'dan buraya gelenler için "9"içine 9, bu benim için çalıştı iyi şeydir: (Integer. "9").
weltschmerz

Yanıtlar:


79

Bu 10pxveya üzerinde çalışacakpx10

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

yalnızca ilk sürekli basamağı ayrıştırır, böylece

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

Güzel cevap! Bence bu, okuma dizesi kullanmaktan daha iyidir. Tekniğinizi kullanmak için cevabımı değiştirdim. Ben de birkaç küçük değişiklik yaptım.
Benjamin Atkin

bu bana veriyorException in thread "main" java.lang.ClassNotFoundException: Integer.,
maazza

83

Yeni cevap

Snrobot'un cevabını daha çok seviyorum. Java yöntemini kullanmak, bu basit kullanım durumu için okuma dizesi kullanmaktan daha basit ve daha sağlamdır. Birkaç küçük değişiklik yaptım. Yazar negatif sayıları göz ardı etmediği için, onu negatif sayılara izin verecek şekilde ayarladım. Ayrıca, sayının dizenin başlangıcından başlamasını gerektirecek şekilde yaptım.

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

Ek olarak, Tamsayı / parseInt'in, baştaki sıfırlar olsa bile, hiçbir radix verilmediğinde ondalık olarak ayrıştırdığını buldum.

Eski cevap

İlk olarak, sadece bir tamsayıyı ayrıştırmak için (çünkü bu google'da bir hit ve iyi bir arka plan bilgisi):

Okuyucuyu kullanabilirsiniz :

(read-string "9") ; => 9

Okunduktan sonra bir sayı olup olmadığını kontrol edebilirsiniz:

(defn str->int [str] (if (number? (read-string str))))

Kullanıcı girdisine clojure okuyucu tarafından güvenilip güvenilemeyeceğinden emin değilim, bu yüzden okumadan önce de kontrol edebilirsiniz:

(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))

Sanırım son çözümü tercih ediyorum.

Ve şimdi, özel sorunuza gelelim. Tam sayı ile başlayan bir şeyi ayrıştırmak için, örneğin 29px:

(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29

Cevabınızı en çok beğendim - ne yazık ki bu clojure çekirdek kitaplığı tarafından sağlanmıyor. Bir küçük eleştiri - teknik senin ifbir olmalı whensenin fns hiçbir başka blok olmadığı için.
quux00

1
Evet, lütfen birinci veya ikinci kod parçacığından sonra okumayı bırakmayın!
Benjamin Atkin

2
Başında sıfır bulunan sayılarla ilgili bir uyarı. read-stringonları sekizlik olarak yorumlar: (read-string "08")bir istisna atar. Integer/valueOfbunları ondalık sayı olarak (Integer/valueOf "08")değerlendirir : 8 olarak değerlendirilir.
rubasov

Ayrıca read-stringboş bir dize veya "29px" gibi bir şey verirseniz bir istisna atacağını unutmayın
Ilya Boyandin

Olması gerektiği gibi. Soru gövdesindeki soruyu cevaplamadan önce başlıktaki soruyu ve insanların bu sayfayı gördüklerinde ne beklediklerini cevapladım. Cevabımın gövdesindeki son kod parçası.
Benjamin Atkin

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

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

Teşekkürler. Bu, bir ürünü bir basamak dizisine bölmemde yardımcı oldu.
octopusgrabbus

3
Bu cevap için Java ülkesinde olduğumuzdan Integer/valueOf, Integer kurucusundan ziyade genellikle kullanılması tavsiye edilir . Integer sınıfı, nesne oluşturmayı en aza indirmek için -128 ile 127 arasındaki değerleri önbelleğe alır. Tamsayı Javadoc, bunu şu
gönderide

15

Bu benim için çok daha basit bir şekilde çalışıyor.

(okuma dizesi "123")

=> 123


1
Bunu kullanıcı girdisiyle kullanırken dikkatli olun. read-stringaşağıdaki belgelere göre kod çalıştırabilir: clojuredocs.org/clojure.core/read-string
jerney

bu, örneğin bir programlama bulmacası gibi güvenilir girdi için harikadır. @jerney haklı: gerçek kodda kullanmamaya dikkat edin.
hraban

10

AFAIK, sorununuz için standart bir çözüm yok. Bence aşağıdaki gibi kullanılan bir şey clojure.contrib.str-utils2/replaceyardımcı olmalı:

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

Tavsiye edilmez. Biri üzerine atana kadar çalışacak 1.5... ve aynı zamanda yerleşik clojure.string/replaceişlevi kullanmayacak.
Katran

8

Bu mükemmel değil, ama burada bir şey var filter, Character/isDigitve Integer/parseInt. Kayan nokta numaraları için çalışmaz ve girişte rakam yoksa başarısız olur, bu yüzden muhtemelen temizlemelisiniz. Umarım bunu yapmanın çok fazla Java içermeyen daha güzel bir yolu vardır.

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

Muhtemelen gereksinimlere birkaç şey eklerim:

  • Bir rakamla başlamalı
  • Boş girdileri tolere etmelidir
  • Toleratlar herhangi bir nesneden geçirilir (toString standarttır)

Belki şöyle bir şey:

(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

ve sonra bunu, 0 dışında bir kullanıcı tarafından sağlanan varsayılana izin veren bir çoklu yöntem yapmak için bonus puanlar.


4

Snrobot'un cevabını genişleterek:

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

Bu sürümler, girişte bir istisna oluşturmak yerine rakam yoksa sıfır döndürür.

Sorum, adın "str-> int" olarak kısaltılmasının kabul edilebilir olup olmadığı veya bunun gibi şeylerin her zaman tam olarak belirtilmesi gerekip gerekmediğidir.


3

Ayrıca (re-seq)işlevin kullanılması , dönüş değerini, sırayla giriş dizesinde bulunan tüm sayıları içeren bir dizeye genişletebilir:

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

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

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


3

Soru, bir dizeyi sayıya ayrıştırmayı sorar.

(number? 0.5)
;;=> true

Dolayısıyla, yukarıdaki ondalık sayılardan da ayrıştırılması gerekir.

Belki şu anda soruyu tam olarak yanıtlamıyor olabilirim, ancak genel kullanım için bunun bir sayı olup olmadığı konusunda katı olmak isteyeceğinizi (bu nedenle "piksel" e izin verilmez) ve arayanın sıfır döndürerek sayı olmayanları işlemesine izin vereceğinizi düşünüyorum:

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

Ve eğer Floats, alanınız için Float/parseFloatkoymak bigdecveya başka bir şey yerine sorunluysa .


3

Daha normal bir String hazır bilgisini bir sayıya, yani başka sayısal olmayan karakterleri olmayan bir dizeye ayrıştırmak isteyen herkes için. Bunlar en iyi iki yaklaşımdır:

Java birlikte çalışmasının kullanılması:

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

Bu, kullanım durumunuz için önemli olduğunda, numarayı ayrıştırmak istediğiniz türü tam olarak kontrol etmenizi sağlar.

Clojure EDN okuyucunun kullanılması:

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

Kullanılarak farklı read-stringmesafede clojure.coreolan güvenilir olmayan girişi kullanımı güvenli değildir, edn/read-stringörneğin kullanıcı girdisi gibi güvenilir olmayan giriş çalıştırmak için güvenlidir.

Türler üzerinde belirli bir denetime sahip olmanız gerekmiyorsa, bu genellikle Java birlikte çalışmasından daha uygundur. Clojure'un aşağıdaki gibi ayrıştırabileceği herhangi bir sayıyı ayrıştırabilir:

;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")

Tam liste burada: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers


2

Basit durumlarda, yukarıda belirtildiği gibi ilk rakam dizisini çıkarmak için bir normal ifade kullanabilirsiniz.

Daha karmaşık bir durumunuz varsa, InstaParse kütüphanesini kullanmak isteyebilirsiniz:

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

Ayrıca, sadece merak edilen bir soru: (t/refer-tupelo)kullanıcıya yaptırmak yerine neden kullanıyorsunuz (:require [tupelo.core :refer :all])?
Qwerp-Derp

refer-tupelorefer-clojureher şeyi içermediği için modellenmiştir (:require [tupelo.core :refer :all]).
Alan Thompson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.