J'ai plusieurs chaînes, certaines comme "45", d'autres comme "45px". Comment convertir les deux en nombre 45?
"9"
en 9
, c'est la meilleure chose qui a fonctionné pour moi: (Integer. "9")
.
J'ai plusieurs chaînes, certaines comme "45", d'autres comme "45px". Comment convertir les deux en nombre 45?
"9"
en 9
, c'est la meilleure chose qui a fonctionné pour moi: (Integer. "9")
.
Réponses:
Cela fonctionnera sur 10px
oupx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
il analysera le premier chiffre continu uniquement ainsi
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
J'aime mieux la réponse de snrobot. L'utilisation de la méthode Java est plus simple et plus robuste que l'utilisation de la chaîne de lecture pour ce cas d'utilisation simple. J'ai fait quelques petits changements. Puisque l'auteur n'a pas exclu les nombres négatifs, je l'ai ajusté pour autoriser les nombres négatifs. Je l'ai également fait pour que le numéro commence au début de la chaîne.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
De plus, j'ai trouvé que Integer / parseInt analyse comme décimal lorsqu'aucune base n'est donnée, même s'il y a des zéros non significatifs.
Tout d'abord, pour analyser juste un entier (puisqu'il s'agit d'un hit sur google et que ce sont de bonnes informations de base):
Vous pouvez utiliser le lecteur :
(read-string "9") ; => 9
Vous pouvez vérifier qu'il s'agit d'un nombre après sa lecture:
(defn str->int [str] (if (number? (read-string str))))
Je ne suis pas sûr que l'entrée utilisateur puisse être approuvée par le lecteur clojure afin que vous puissiez également vérifier avant de la lire:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Je pense que je préfère la dernière solution.
Et maintenant, à votre question spécifique. Pour analyser quelque chose qui commence par un entier, comme 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
devriez être un when
car il n'y a pas d'autre bloc dans votre fns.
read-string
les interprète comme octaux: (read-string "08")
lève une exception. Integer/valueOf
les traite comme des nombres décimaux: donne la valeur (Integer/valueOf "08")
8.
read-string
lève une exception si vous lui donnez une chaîne vide ou quelque chose comme "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, plutôt que le constructeur Integer. La classe Integer met en cache les valeurs comprises entre -128 et 127 pour minimiser la création d'objets. Le Javadoc entier décrit cela comme le fait cet article: stackoverflow.com/a/2974852/871012
Cela fonctionne en réponse pour moi, beaucoup plus simple.
(chaîne de lecture "123")
=> 123
read-string
peut exécuter du code selon la documentation: clojuredocs.org/clojure.core/read-string
AFAIK il n'y a pas de solution standard à votre problème. Je pense que quelque chose comme ce qui suit, qui utilise clojure.contrib.str-utils2/replace
, devrait aider:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... et il n'utilise pas non plus la clojure.string/replace
fonction intégrée.
Ce n'est pas parfait, mais voici quelque chose avec filter
, Character/isDigit
et Integer/parseInt
. Cela ne fonctionnera pas pour les nombres à virgule flottante et échouera s'il n'y a pas de chiffre dans l'entrée, vous devriez donc probablement le nettoyer. J'espère qu'il existe une meilleure façon de faire cela qui n'implique pas autant de 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)
J'ajouterais probablement quelques éléments aux exigences:
Peut-être quelque chose comme:
(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
et puis peut-être des points bonus pour en faire une méthode multiple qui permet une valeur par défaut fournie par l'utilisateur autre que 0.
Extension de la réponse de snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Cette version renvoie nil s'il n'y a pas de chiffres dans l'entrée, plutôt que de lever une exception.
Ma question est de savoir s'il est acceptable d'abréger le nom en "str-> int", ou si des choses comme celle-ci doivent toujours être entièrement spécifiées.
L'utilisation de la (re-seq)
fonction peut également étendre la valeur de retour à une chaîne contenant tous les nombres existants dans la chaîne d'entrée dans l'ordre:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
La question porte sur l'analyse d'une chaîne en un nombre.
(number? 0.5)
;;=> true
Donc, à partir des décimales ci-dessus, il faut également analyser.
Peut-être ne répond pas exactement à la question maintenant, mais pour un usage général, je pense que vous voudriez être strict quant à savoir s'il s'agit d'un nombre ou non (donc "px" n'est pas autorisé) et laisser l'appelant gérer les non-nombres en renvoyant nil:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
Et si les flottants sont problématiques pour votre domaine au lieu de Float/parseFloat
put bigdec
ou autre chose.
Pour tous ceux qui cherchent à analyser un littéral String plus normal en un nombre, c'est-à-dire une chaîne qui n'a pas d'autres caractères non numériques. Voici les deux meilleures approches:
Utilisation de l'interopérabilité 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")
Cela vous permet de contrôler précisément le type dans lequel vous souhaitez analyser le nombre, lorsque cela compte pour votre cas d'utilisation.
Utilisation du lecteur Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Contrairement à l'utilisation read-string
de clojure.core
qui n'est pas sûre à utiliser sur une entrée non approuvée, elle edn/read-string
peut être exécutée en toute sécurité sur une entrée non approuvée telle que l'entrée utilisateur.
C'est souvent plus pratique que l'interopérabilité Java si vous n'avez pas besoin de contrôler spécifiquement les types. Il peut analyser n'importe quel nombre littéral que Clojure peut analyser, tel que:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Liste complète ici: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Pour les cas simples, vous pouvez simplement utiliser une expression régulière pour extraire la première chaîne de chiffres comme mentionné ci-dessus.
Si vous avez une situation plus compliquée, vous souhaiterez peut-être utiliser la bibliothèque 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)
au lieu de faire faire à l'utilisateur (:require [tupelo.core :refer :all])
?
refer-tupelo
est calqué sur le modèle refer-clojure
, en ce sens qu'il n'inclut pas tout ce qu'il (:require [tupelo.core :refer :all])
fait.