Ho varie stringhe, alcune come "45", altre come "45px". Come posso convertire entrambi al numero 45?
"9"
in 9
, questa è la cosa migliore che ha funzionato per me: (Integer. "9")
.
Ho varie stringhe, alcune come "45", altre come "45px". Come posso convertire entrambi al numero 45?
"9"
in 9
, questa è la cosa migliore che ha funzionato per me: (Integer. "9")
.
Risposte:
Questo funzionerà su 10px
opx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
analizzerà solo la prima cifra continua
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Mi piace la risposta di Snrobot meglio. L'uso del metodo Java è più semplice e più robusto rispetto all'utilizzo della stringa di lettura per questo semplice caso d'uso. Ho fatto un paio di piccoli cambiamenti. Poiché l'autore non ha escluso i numeri negativi, l'ho modificato per consentire i numeri negativi. Ho anche fatto in modo che richieda il numero per iniziare all'inizio della stringa.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Inoltre ho scoperto che Integer / parseInt viene analizzato come decimale quando non viene fornita alcuna radice, anche se ci sono zero iniziali.
Innanzitutto, per analizzare solo un numero intero (poiché si tratta di un hit su google ed è una buona informazione di base):
Puoi usare il lettore :
(read-string "9") ; => 9
Puoi verificare che sia un numero dopo che è stato letto:
(defn str->int [str] (if (number? (read-string str))))
Non sono sicuro che l'input dell'utente possa essere considerato attendibile dal lettore di clojure in modo da poter verificare anche prima che venga letto:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Penso di preferire l'ultima soluzione.
E ora, alla tua domanda specifica. Per analizzare qualcosa che inizia con un numero intero, come 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
dovresti esserlo when
perché non ci sono altri blocchi nelle tue reti.
read-string
li interpreta come ottale: (read-string "08")
genera un'eccezione. Integer/valueOf
li considera come decimali: (Integer/valueOf "08")
valuta 8.
read-string
genera un'eccezione se le dai una stringa vuota o qualcosa del genere "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, piuttosto che il costruttore Integer. La classe Integer memorizza nella cache valori compresi tra -128 e 127 per ridurre al minimo la creazione di oggetti. L'intero Javadoc descrive questo come fa questo post: stackoverflow.com/a/2974852/871012
Questo funziona in cambio di me, molto più semplice.
(stringa di lettura "123")
=> 123
read-string
può eseguire il codice in base ai documenti: clojuredocs.org/clojure.core/read-string
AFAIK non esiste una soluzione standard per il tuo problema. Penso che qualcosa di simile al seguente, che utilizza clojure.contrib.str-utils2/replace
, dovrebbe aiutare:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... e inoltre non utilizza la clojure.string/replace
funzione integrata.
Questo non è perfetto, ma ecco qualcosa con filter
, Character/isDigit
e Integer/parseInt
. Non funzionerà con numeri a virgola mobile e non funzionerà se non ci sono cifre nell'input, quindi probabilmente dovresti ripulirlo. Spero che ci sia un modo migliore di farlo che non coinvolga così tanto 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)
Vorrei probabilmente aggiungere alcune cose ai requisiti:
Forse qualcosa del tipo:
(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
e quindi forse punti bonus per rendere questo un multi-metodo che consente un valore predefinito fornito dall'utente diverso da 0.
Espandendo la risposta di Snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Questa versione restituisce zero se non ci sono cifre nell'input, piuttosto che sollevare un'eccezione.
La mia domanda è se sia accettabile abbreviare il nome in "str-> int", o se cose come questa debbano sempre essere specificate per intero.
Anche l'uso della (re-seq)
funzione può estendere il valore restituito a una stringa contenente tutti i numeri esistenti nella stringa di input in ordine:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
La domanda si pone sull'analisi di una stringa in un numero.
(number? 0.5)
;;=> true
Quindi dai decimali sopra dovrebbero essere analizzati anche.
Forse non rispondo esattamente alla domanda ora, ma per uso generale penso che vorresti essere severo sul fatto che sia un numero o meno (quindi "px" non è consentito) e lasciare che il chiamante gestisca i non-numeri restituendo zero:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
E se i float sono problematici per il tuo dominio anziché Float/parseFloat
put bigdec
o qualcos'altro.
Per chiunque cerchi di analizzare un valore letterale String più normale in un numero, ovvero una stringa che non ha altri caratteri non numerici. Questi sono i due migliori approcci:
Utilizzando l'interoperabilità 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")
Ciò consente di controllare con precisione il tipo in cui si desidera analizzare il numero, quando ciò è importante per il proprio caso d'uso.
Utilizzando il lettore EDN Clojure:
(require '[clojure.edn :as edn])
(edn/read-string "333")
A differenza dell'utilizzo read-string
da clojure.core
cui non è sicuro utilizzare input non attendibili, edn/read-string
è possibile eseguire input non attendibili come l'input dell'utente.
Questo è spesso più conveniente dell'interoperabilità Java se non è necessario avere un controllo specifico dei tipi. Può analizzare qualsiasi numero letterale che Clojure può analizzare come:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Elenco completo qui: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Per casi semplici puoi semplicemente usare una regex per estrarre la prima stringa di cifre come menzionato sopra.
Se hai una situazione più complicata, potresti voler usare la libreria 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)
invece di far fare all'utente (:require [tupelo.core :refer :all])
?
refer-tupelo
è modellato dopo refer-clojure
, in quanto non include tutto ciò che (:require [tupelo.core :refer :all])
fa.