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 10pxopx10
(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
ifdovresti esserlo whenperché non ci sono altri blocchi nelle tue reti.
read-stringli interpreta come ottale: (read-string "08")genera un'eccezione. Integer/valueOfli considera come decimali: (Integer/valueOf "08")valuta 8.
read-stringgenera 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-stringpuò 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/replacefunzione integrata.
Questo non è perfetto, ma ecco qualcosa con filter, Character/isDigite 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/parseFloatput bigdeco 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-stringda clojure.corecui 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.